Building a Team of AI Agents with OpenClaw
Read from search, close with the playbook
If this post helped, here is the fastest path into the full operator setup.
Search posts do the first job. The preview, homepage, and full playbook show how the pieces fit together when you want the whole operating system.
Most people start with one AI agent. That's fine — one agent handling your Slack, your tasks, your coding is already a force multiplier. But at some point you hit a wall. One agent can't be everywhere at once, and one personality doesn't fit every context.
OpenClaw solves this with multi-agent routing: multiple fully isolated agents running inside a single gateway process, each with its own workspace, persona, channel accounts, and session history. From the outside they look like a coordinated team. Under the hood they're cleanly separated — no cross-talk unless you explicitly enable it.
This guide walks you through how to build that team — from the concepts to real config examples to spawning ACP coding sessions.
What "one agent" actually means
In OpenClaw, an agent is a fully scoped runtime with its own:
- Workspace — the directory where its files live:
SOUL.md,AGENTS.md,USER.md, memory files, and any workspace-level skills - State directory (
agentDir) — auth profiles, model registry, per-agent config - Session store — chat history under
~/.openclaw/agents/<agentId>/sessions/
Each agent reads from its own ~/.openclaw/agents/<agentId>/agent/auth-profiles.json. Credentials are not shared automatically. If two agents need the same credentials, you copy the file explicitly.
The default setup is single-agent: one agent with id main, workspace at ~/.openclaw/workspace. When you add more agents, they sit alongside main — same process, separate brains.
Why you'd want multiple agents
A few patterns that push people toward multi-agent setups:
- Different channels, different personalities. Your work agent on Telegram should feel different from your personal agent on WhatsApp. Different
SOUL.md, different model, different tool access. - Different people, same server. Multiple people can share one OpenClaw host while keeping their AI data fully isolated — each person gets their own agent, their own session history, their own credentials.
- Specialization. One fast Sonnet agent for quick replies, one Opus agent for deep work. Route by channel or by specific contacts.
- Security isolation. A sandboxed agent for untrusted channels (family groups, public bots) alongside an unsandboxed agent for your personal use.
Adding a second agent
The quickest path is the wizard:
openclaw agents add coding
openclaw agents add social
Each command creates a workspace with the standard bootstrap files, a dedicated agentDir, and a session store. Then verify:
openclaw agents list --bindings How routing works
Messages are routed to agents via bindings. A binding matches an inbound message by channel, account, and peer (DM or group id), then sends it to the specified agentId.
Bindings are deterministic — most-specific match wins:
- Exact peer match (specific DM or group id)
- Parent peer match (thread inheritance)
- Guild + roles (Discord role routing)
- Guild-level (Discord server)
- Team-level (Slack team)
- Account-level (specific account id for a channel)
- Channel-wide fallback (
accountId: "*") - Default agent (first in list, or marked
default: true)
If multiple bindings match at the same specificity, the first one in config order wins. Multiple match fields in a single binding use AND semantics — all specified fields must match.
Example: fast agent on WhatsApp, deep-work agent on Telegram
A clean two-agent split by channel:
{
agents: {
list: [
{
id: "chat",
name: "Everyday",
workspace: "~/.openclaw/workspace-chat",
model: "anthropic/claude-sonnet-4-5",
},
{
id: "opus",
name: "Deep Work",
workspace: "~/.openclaw/workspace-opus",
model: "anthropic/claude-opus-4-6",
},
],
},
bindings: [
{ agentId: "chat", match: { channel: "whatsapp" } },
{ agentId: "opus", match: { channel: "telegram" } },
],
}
Every WhatsApp message hits the Sonnet agent. Every Telegram message hits Opus. Each has its own workspace, so they get different SOUL.md files — different personalities, different operating instructions.
Example: routing one DM to a different agent
Keep WhatsApp on the fast agent, but route one specific contact to Opus:
{
bindings: [
{
agentId: "opus",
match: {
channel: "whatsapp",
peer: { kind: "direct", id: "+15551234567" },
},
},
{ agentId: "chat", match: { channel: "whatsapp" } },
],
} Peer bindings always win over channel-wide rules, so order them first in the config.
Example: multiple Discord bots, multiple agents
Each Discord bot account maps to a unique accountId. Bind each to a different agent:
{
agents: {
list: [
{ id: "main", workspace: "~/.openclaw/workspace-main" },
{ id: "coding", workspace: "~/.openclaw/workspace-coding" },
],
},
bindings: [
{ agentId: "main", match: { channel: "discord", accountId: "default" } },
{ agentId: "coding", match: { channel: "discord", accountId: "coding" } },
],
channels: {
discord: {
accounts: {
default: {
token: "DISCORD_BOT_TOKEN_MAIN",
guilds: {
"111111111111111111": {
channels: { "222222222222222222": { allow: true, requireMention: false } },
},
},
},
coding: {
token: "DISCORD_BOT_TOKEN_CODING",
guilds: {
"111111111111111111": {
channels: { "333333333333333333": { allow: true, requireMention: false } },
},
},
},
},
},
},
}
Two bot tokens, two agent brains, one gateway. The coding agent can have its SOUL.md tuned for technical work while the main agent handles general conversation.
Per-agent sandbox and tool restrictions
You can lock down specific agents without affecting others. This is useful for untrusted contexts — a public-facing bot or a family group chat shouldn't have the same tool access as your personal agent:
{
agents: {
list: [
{
id: "personal",
workspace: "~/.openclaw/workspace-personal",
sandbox: { mode: "off" },
// No tool restrictions
},
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: {
mode: "all",
scope: "agent",
docker: {
setupCommand: "apt-get update && apt-get install -y git curl",
},
},
tools: {
allow: ["read", "sessions_list", "sessions_history"],
deny: ["exec", "write", "edit", "browser"],
},
},
],
},
} The family agent runs in a Docker sandbox, can only read files and list sessions — no exec, no writes, no browser access. The personal agent runs unrestricted on the host.
ACP coding sessions: agents that write code
OpenClaw supports ACP (Agent Client Protocol) — a way to run external coding harnesses like Claude Code, Codex, Gemini CLI, and Pi through your agent. ACP sessions are a different tier from the regular sub-agent runtime.
The practical difference:
- Sub-agents: OpenClaw-native delegated runs. Use for general task delegation.
- ACP sessions: External harness runtimes (Codex, Claude Code, etc.). Use when you need a full coding agent with file access and shell execution.
Starting an ACP session from chat
/acp spawn codex --mode persistent --thread auto This creates a persistent Codex session and binds it to the current thread. Follow-up messages in that thread route to Codex until you close or unfocus it.
Spawning an ACP session programmatically
From inside an agent turn (e.g., a cron job or another agent):
{
task: "Open the repo and fix the failing tests",
runtime: "acp",
agentId: "codex",
thread: true,
mode: "session"
}
Set runtime: "acp" explicitly — it defaults to "subagent" otherwise.
Supported harnesses
The acpx backend (installed separately) supports these harness aliases:
piclaude(Claude Code)codex(OpenAI Codex)opencodegemini(Gemini CLI)kimi
Install the backend plugin first:
openclaw plugins install acpx
openclaw config set plugins.entries.acpx.enabled true
Then verify with /acp doctor.
Permission modes for ACP sessions
ACP sessions are non-interactive — there's no TTY to approve prompts. Set permissionMode appropriately:
openclaw config set plugins.entries.acpx.config.permissionMode approve-all
openclaw config set plugins.entries.acpx.config.nonInteractivePermissions fail
The default (approve-reads) blocks write operations in ACP sessions. For coding agents that need to write files, set approve-all. Restart the gateway after changing these.
Persistent ACP bindings for channels
You can bind a Discord channel or Telegram topic permanently to an ACP session in your config:
{
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "channel", id: "222222222222222222" },
},
acp: { label: "codex-main", mode: "persistent" },
},
],
channels: {
discord: {
threadBindings: {
enabled: true,
spawnAcpSessions: true,
},
},
},
} Messages in that Discord channel route directly to the persistent Codex session. It's like having a dedicated coding channel that always knows the full context of what you're building.
Agent-to-agent messaging
By default, agents cannot message each other — this is intentional for isolation. To enable it explicitly:
{
tools: {
agentToAgent: {
enabled: true,
allow: ["main", "coding"],
},
},
}
Only agents listed in allow can communicate. This lets you build orchestration patterns — your main agent spawning a coding agent to handle a task, then receiving the result — while keeping the blast radius controlled.
Putting it together: a real team setup
Here's a representative setup — a main personal agent, a coding agent via ACP, and a sandboxed family agent:
{
agents: {
list: [
{
id: "main",
default: true,
workspace: "~/.openclaw/workspace",
model: "anthropic/claude-opus-4-6",
},
{
id: "coding",
workspace: "~/.openclaw/workspace-coding",
model: "anthropic/claude-sonnet-4-5",
runtime: {
type: "acp",
acp: { agent: "codex", mode: "persistent", cwd: "/workspace/repos" },
},
},
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: { mode: "all", scope: "agent" },
tools: {
deny: ["exec", "write", "edit", "browser"],
},
},
],
},
bindings: [
{ agentId: "main", match: { channel: "telegram" } },
{
agentId: "coding",
match: {
channel: "discord",
peer: { kind: "channel", id: "333333333333333333" },
},
},
{
agentId: "family",
match: {
channel: "whatsapp",
peer: { kind: "group", id: "120363999999999999@g.us" },
},
},
{ agentId: "main", match: { channel: "whatsapp" } },
],
tools: {
agentToAgent: {
enabled: true,
allow: ["main", "coding"],
},
},
}
Three brains. Three workspaces. One gateway. Restart with openclaw gateway restart and you're running a team.
One thing people get wrong
Auth profiles are per-agent. If you add a second agent and wonder why it can't access your Slack or GitHub — it's because credentials aren't shared. Copy ~/.openclaw/agents/main/agent/auth-profiles.json to the new agent's agentDir, or re-run the relevant auth setup for that agent explicitly.
Similarly: never share the same agentDir between two agents. It causes session and auth collisions that are annoying to debug.
Final thoughts
The jump from one agent to a team isn't that big — it's mostly config. What changes is the mental model. You stop thinking about "my AI agent" as a single thing and start thinking about it as a staffing problem: who handles what, with what access, on what channel.
Once you've been running two or three agents for a week, going back to one feels like losing a team member.
Want the complete setup guide with config templates and workflow examples? Get The OpenClaw Playbook — $9.99