OpenClaw multi agents and multiple models are two different layers of one config file: several agents are keyed entries under agents.entries, each owning its own workspace, state directory and session store, while several models are per-agent model values inside those entries. Two more layers sit alongside them — bindings decides which agent answers, and a fallback chain decides what one agent does when its model fails. Editing the wrong layer is the single most common reason a change appears to do nothing.
Running more agents costs nothing in software. OpenClaw's documentation overview states it is developed in the open by "the OpenClaw Foundation, an independent 501(c)(3)" with "No paid tier, no telemetry by default beyond a version check you can turn off, no lab owns it", and openclaw.ai adds "No subscription. No hosted tier. No token." The npm package openclaw is MIT, with latest at 2026.9.5 beside an extended-stable channel at 2026.7.35 and engines.node of >=24.16.0 <25 || >=26.1.0 (npm registry, checked September 21, 2026). What a second agent adds to your bill is tokens.
OpenClaw multi agents: four layers, and the symptom when you edit the wrong one
| Layer | Config key | What it decides | Symptom when this is the layer you actually needed |
|---|---|---|---|
| Agent roster | agents.entries.<id> | Separate workspace, state directory, session store, skills and tool policy | Two personas keep reading each other's notes and history |
| Channel routing | bindings[] | Which agent answers an inbound message on which channel or account | Routing reports AGENT_SELECTION_REQUIRED |
| Model choice | agents.entries.<id>.model | Which model that agent's turns run on | A /model change in one chat left every other chat unchanged |
| Fallback chain | model.fallbacks, agents.defaults.model | Which model takes over on a provider-side failure | A context-overflow error never failed over, because it is not a failover trigger |
All four were read from OpenClaw's own documentation on September 21, 2026: entries and multi-agent, agent bindings and model failover. Two shapes in older tutorials are stale: an agents.list array roster is the legacy form Doctor migrates, and a default: true marker on an entry is retired — the entries page states plainly that "default is retired" and that multi-agent operations need a binding or an explicit target. OpenClaw also ran under two earlier names, so any Moltbot- or Clawdbot-era config you find predates this schema.
OpenClaw multiple agents setup: a minimal two-agent, two-model config
This fragment assumes you already have a working models.providers block — best API for OpenClaw carries the Kunavo one, including api: "anthropic-messages" and the base URL published at Anthropic base URL. What follows is only the agent and routing layer.
{
"agents": {
"defaults": {
"modelSelectionScope": "session",
"model": {
"primary": "kunavo/claude-haiku-4-5",
"fallbacks": [
"kunavo/claude-sonnet-5"
]
}
},
"entries": {
"ops": {
"name": "Ops",
"workspace": "~/.openclaw/workspace-ops",
"agentDir": "~/.openclaw/agents/ops/agent",
"model": "kunavo/claude-haiku-4-5",
"modelPolicy": {
"allow": [
"kunavo/claude-haiku-4-5"
]
}
},
"build": {
"name": "Build",
"workspace": "~/.openclaw/workspace-build",
"agentDir": "~/.openclaw/agents/build/agent",
"model": {
"primary": "kunavo/claude-opus-5",
"fallbacks": [
"kunavo/claude-sonnet-5"
]
},
"utilityModel": "kunavo/claude-haiku-4-5"
}
}
},
"bindings": [
{
"agentId": "build",
"match": {
"channel": "discord",
"accountId": "build"
}
},
{
"agentId": "ops",
"match": {
"channel": "discord",
"accountId": "*"
}
}
]
}Four things in that block are load-bearing. Each agent has its own agentDir, because the multi-agent page warns "Never reuse agentDir across agents — it causes auth/session state collisions." The ops agent uses the string form of model, which the entries page defines as "a strict per-agent primary with no model fallback" — so a failure surfaces instead of silently moving routine work to a pricier tier. The build agent uses the object form with an explicit fallbacks list, which is how you opt an agent in; the failover page adds that an agent can set model: { fallbacks: [...] } alone and keep inheriting the shared primary. And the narrow binding sits above the wildcard, because within one match tier "the first matching bindings entry wins."
Workspace defaults differ between the default agent and the rest, and are worth setting explicitly: the default agent's workspace is <stateDir>/workspace while other agents default to <stateDir>/workspace-<agentId>. Memory follows the workspace, since OpenClaw's built-in engine "remembers things by writing plain Markdown files in your agent's workspace" — so isolating workspaces is what isolates memory. Session permission modes are a separate axis again: read-only, guarded, workspace and full, where "full requires operator.admin. The other modes require operator.write" (permission modes, September 21, 2026). A cheap model on a permissive agent is still a permissive agent.
What separate agents separate — and what they do not
| Thing | Per agent? | Where it lives |
|---|---|---|
| Workspace files and Markdown memory | Yes | agents.entries.*.workspace |
| Chat history | Yes | <agentDir>/openclaw-agent.sqlite |
| Stored auth profiles | Yes | agentDir; auth mutations require --agent |
| Skills | Yes | An explicit agents.entries.*.skills list replaces the defaults rather than merging |
| Tools, sandbox, elevated | Yes | Per-agent keys exist, but precedence differs per key — tools.elevated, for one, "can only further restrict" |
| Model primary, fallbacks, allowlist | Yes | agents.entries.*.model, .modelPolicy.allow |
Provider baseUrl, apiKey, dialect | No | models.providers is Gateway-wide |
| Provider keys from the environment | No | One Gateway process, one environment |
openclaw models set | No | Global; it rejects --agent and writes agent defaults |
This is the boundary the price tables miss. Separate entries give you separate files, memory, history, tool policy and stored auth profiles. They do not, by themselves, give each agent its own API key for an environment-configured custom provider — the documented per-agent schema has no baseUrl, apiKey or providers field at all. Absence from the documentation is not proof the code forbids it, so read this as not documented; if you need hard key separation per tenant, run separate Gateways. A related limit applies to identity: OpenClaw's WhatsApp DM-split example notes that "Replies still come from the same WhatsApp number — there is no per-agent sender identity", and that "Direct chats collapse to the agent's main session key by default, so true isolation requires one agent per person." That sentence is stated for WhatsApp; check your own channel's page before generalizing it.
One capability boundary to note while you are splitting roles: Kunavo serves no text-to-speech, speech-to-text or embedding model, so an agent that needs voice output or a vector index has to call an outside provider for that step.
OpenClaw multiple models: strict, fallback, policy and the utility lane
Per-agent model selection has four controls worth setting deliberately. model as a string is strict. { primary, fallbacks: [...] } opts that agent into failover. modelPolicy.allow is an allowlist that "replaces the default policy for that agent" — accepting aliases, exact refs and trailing wildcards — which is how you stop a routine agent ever reaching an expensive model. And utilityModel is a separate, usually cheaper model for "short internal tasks such as generated session and thread titles", with a per-agent override.
The documented trigger list is specific. OpenClaw advances on "auth failures, rate limits and cooldown exhaustion, overloaded/provider-busy errors, timeout-shaped failover errors, billing disables, model_not_found", and on other unrecognised errors while candidates remain — but not on context-overflow errors, which stay inside compaction and retry logic, and not on "explicit aborts that are not timeout/failover-shaped". Outside group and channel conversations it is visible: those surfaces post a status notice of the form Model Fallback: <fallback> (selected <primary>; <reason>) and a matching cleared notice, while group and channel conversations "suppress the visible notices while retaining the same fallback state", so do not rely on seeing one in a shared room. An explicit session selection — /model, the model picker, session_status(model=...) or sessions.patch — is strict: if that model fails before producing a reply, OpenClaw reports the failure instead of answering from a configured fallback. A cron job's --model is not one of those; the documentation calls it a job primary that still uses configured fallbacks unless the job sets payload.fallbacks: [].
Two more mechanics decide whether your intent survives. Request params merge through four layers, from agents.defaults.params to agents.entries.*.params, with later layers overriding by key. And parallelism has a computed ceiling: agents.defaults.maxConcurrent defaults to max(8, available CPU parallelism * 4) across sessions, while each session stays serialized — two messages to one agent do not run at once. For choosing which model belongs in which role, Opus vs Sonnet vs Haiku covers the capability side.
Cost attribution by route, not by agent
Because no documented command reports spend per agent, attribute by route. The arithmetic below is illustrative, not a measured bill and not a ceiling. It assumes a 30-day month, the documented 30m heartbeat default (1,440 runs), 300 output tokens per heartbeat, no cache hits, and a main conversation lane of 8M input and 500K output tokens. The ~100K and ~2–5K per-run context figures are OpenClaw's own illustration of what isolatedSession removes, not something measured here; 3,000 is the midpoint. Rates are live Kunavo catalog prices per million tokens.
| Route | Assumed input / output per month | At Claude Haiku 4.5 | At Claude Opus 5 |
|---|---|---|---|
| Heartbeat on the shared session, 30m interval | 144.00M / 0.43M | $58.46 | $292.32 |
| The same heartbeat with isolatedSession: true | 4.32M / 0.43M | $2.59 | $12.96 |
| Main conversation turns | 8.00M / 0.50M | $4.20 | $21.00 |
| utilityModel titles and recaps | 0.20M / 0.02M | $0.12 | $0.60 |
Claude Haiku 4.5 lists $0.40 / $2.00 and Claude Opus 5 lists $2.00 / $10.00 per million input / output tokens on the live catalog. The reading that matters: under these assumptions the scheduled lane dominates. A shared-session heartbeat on the strong model works out to $292.32 a month, against $2.59 for the same cadence with isolatedSession: true on the cheap model. OpenClaw says as much itself — "Heartbeats run full agent turns. Shorter intervals burn more tokens" — and names isolatedSession, lightContext, a cheaper model and target: "none" as the levers.
The cheap-heartbeat trick has a documented failure mode, and it is the reason isolatedSession is the better lever than the model swap alone. Heartbeats "preserve the shared session's existing runtime model after the run completes", so a heartbeat that switched a session to a smaller model can leave it in place for the next main-session turn, which may then report context overflow — OpenClaw's recovery message calls this heartbeat model bleed. The documentation's worked example is a local model with a 32k window, so the size of the risk depends on how much smaller the heartbeat model's context window is than the shared session needs. One scheduling note: the documented default interval is 30m, bumped to 1h only when the resolved auth mode is Anthropic OAuth/token, so a plain API-key route keeps 30m unless you set heartbeat.every yourself. Check your own value before budgeting.
Two caveats on the dollar figures. Kunavo's catalog amount is a billing floor rather than a cap: when the upstream reports its charge, the bill is the greater of catalog cost and upstream cost times the applicable markup. And a custom provider declared without a per-model cost object makes OpenClaw's own readout useless — it defaults to cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, showing $0 while the supplier bills normally. Declare cost, contextWindow and maxTokens on each model you add, and reconcile against the provider ledger. The minimum Kunavo top-up is $10 in prepaid credit — a funding minimum, not a task fee or a subscription. See billing details and AI cost optimization.
Which buying route fits a multi-agent Gateway
| Route | Wins when | What you give up |
|---|---|---|
| One Gateway, one gateway-style provider | Several agents on several model families, one key and one balance | No per-agent key separation; provider definitions and environment keys are shared |
| One Gateway, per-agent stored auth profiles | You want each agent to carry its own credential in its own agentDir | Documented for auth profiles only — endpoint overrides per agent are not in the published schema |
| Separate Gateways per tenant | Hard key, environment and spend separation is the requirement | Two processes, two configs, two upgrade paths |
| Direct vendor account per agent | One vendor all day, and you want that vendor's own caching and batch features | Another family means another account; each has its own rates and controls |
| Local model on the scheduled lane | Bounded heartbeat checks with no per-request charge | Hardware and maintenance, plus the heartbeat model bleed caveat above |
| Subscription agent | Flat-rate heavy daily use suits you better than metered tokens | OpenClaw sells no subscription of its own; it would be a different client |
One note on dialects, because caching is where the money is. The Kunavo block in the sibling guide declares api: "anthropic-messages", which OpenClaw treats as a non-direct Anthropic endpoint. Two documented consequences follow. Implicit Anthropic beta headers are suppressed on such endpoints, so features like interleaved thinking are opt-in through an explicit headers["anthropic-beta"] rather than automatic. And caching has to be asked for: OpenClaw seeds cacheRetention: "short" only for the direct anthropic and anthropic-vertex providers, while "custom anthropic-messages-compatible endpoints" are supported "when cacheRetention is set explicitly" — so set params.cacheRetention yourself rather than assuming a default (prompt caching, September 21, 2026). A separate rule covers the other dialect: an openai-completions route to a non-native endpoint sends "no prompt-cache hints". Verify the reported cache usage on your own route before budgeting recurring context as cache hits. Prompt caching covers the rate side.
Verify it landed where you meant
openclaw config validate
openclaw gateway restart
openclaw agents list --bindings
openclaw models status --agent ops --json --check
openclaw models list --agent buildConfig validation checks shape, and gateway restart reloads it; neither proves a billed request succeeded. openclaw agents list --bindings shows the routing actually loaded — prefer it to --tree, which appears in the concepts pages but not in the CLI command table. openclaw models status --agent <id> explains that agent's configured default, and models list --agent <id> shows its inventory. If models set exits nonzero on an unknown provider, that is the model layer: the provider must be an installed plugin or declared under models.providers. If messages reach no agent, that is the routing layer. If duplicate runs appear once several agents share a channel, OpenClaw documents bot loop protection keys as the guard — the documentation describes prevention, not a root cause, so diagnose before assuming.
Then run one bounded task per agent and read the charge your provider account recorded for it. Kunavo has not runtime-tested OpenClaw, single- or multi-agent: everything above is read from OpenClaw's published documentation, and a published configuration is not a compatibility test. Keep a working route available while you try it. Start from the provider configuration, compare the full operating bill in OpenClaw pricing, and create a Kunavo account when you are ready to fund a key.
FAQ
How do I set up multiple agents in OpenClaw?
Add a keyed entry per agent under agents.entries, give each one its own workspace and its own agentDir, then add a bindings array so inbound messages resolve to an agent. OpenClaw's documentation is explicit that agentDir must never be shared: "Never reuse `agentDir` across agents — it causes auth/session state collisions." The CLI equivalent is `openclaw agents add <id>` with --workspace, --agent-dir, --model and a repeatable --bind. Two shapes you may meet in older tutorials are out of date: an agents.list roster is the legacy form that Doctor migrates, and a `default: true` marker on an entry is retired — multi-agent selection now goes through a binding or an explicit target. Read at docs.openclaw.ai on September 21, 2026; not runtime tested here.
Can each OpenClaw agent use a different model?
Yes. agents.entries.<id>.model sets that agent's primary, and the form you write decides whether it can fall back. OpenClaw's documentation states that the "String form sets a strict per-agent primary with no model fallback; object form { primary } is also strict unless you add fallbacks." So a plain string on a per-agent model means a provider error surfaces as an error rather than quietly moving that agent onto a different price tier. Use { primary, fallbacks: [...] } to opt an agent in, and { primary, fallbacks: [] } to make strict behavior explicit. Model refs are always provider-qualified as provider/model. Checked September 21, 2026.
Can each agent have its own API key or provider endpoint?
The endpoint, not through the documented per-agent schema; the credential, yes. models.providers — where baseUrl, apiKey and the api dialect live — is a Gateway-wide block, so every agent in one Gateway shares the same provider definitions, and a key written there as an environment reference resolves from that one Gateway process environment. The per-agent entry schema published on September 21, 2026 has no baseUrl, apiKey or providers field, and agents.entries.*.models carries only params, agentRuntime and codeMode. What is per-agent is the stored auth profile in that agent's agentDir, which holds api_key, token and OAuth credentials: models auth subcommands accept --agent, and auth mutations require it when multiple agents are configured. Absence from the documentation is not proof the code forbids a per-agent endpoint, so treat the endpoint side as not documented rather than impossible. If you need hard separation per tenant, run separate Gateways.
Why did changing the model in chat not change anything?
Because the default write scope is the session you typed in. OpenClaw documents agents.defaults.modelSelectionScope as defaulting to "session": "changing a model in one chat does not change other chats or the configured default, including when the caller is an owner/admin." Use /model with -a/--agent to write the agent's primary or -g/--global for the shared default. Note also that the `openclaw models set` CLI is global and rejects --agent, so it cannot be used to set one agent's model — edit agents.entries.<id>.model instead. Documented behavior checked September 21, 2026.
What does AGENT_SELECTION_REQUIRED mean?
It means routing found no binding for that inbound message and refused to guess. OpenClaw's documentation states that with several agents configured, "If none is available in a multi-agent setup, routing reports AGENT_SELECTION_REQUIRED and asks you to add a binding." The documented match order runs match.peer, match.guildId, match.teamId, an exact match.accountId, then accountId "*" — and ends in a sole-agent fallback that applies "only when exactly one agent is configured; explicit multi-agent fleets without a matching binding fail closed." There is no catch-all owner once you have two agents. Within a tier, "the first matching bindings entry wins", so put narrow rules above broad ones. Inspect what is actually loaded with `openclaw agents list --bindings`. Checked September 21, 2026.
How do I see what each OpenClaw agent cost?
No command documented on September 21, 2026 reports spend broken out by agent, so attribute by model and by route instead — main turns, heartbeat runs, the utilityModel lane and spawned subagents. Two cautions about the local figures. OpenClaw's dollar readouts are estimates computed from its own local pricing metadata — its usage surfaces do pull provider-reported plan and spend data where a provider exposes it, but the per-session cost analysis is session-derived — and /usage cost warns that its Today and Last 30d totals may be incomplete while the aggregate cache is refreshing, partial or stale. And for a custom provider declared without a per-model cost object, OpenClaw defaults to cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } — a $0 readout for requests your supplier is billing normally. Reconcile against the provider ledger, not the chat footer.
OpenClaw documentation, CLI reference and the npm registry entry checked September 21, 2026 at package version 2026.9.5; no Gateway, agent, binding or paid request was exercised here. Kunavo token rates are read from the live catalog, and every dollar figure on this page is illustrative token arithmetic rather than a measured bill.