Chapter 5: Workspaces & Multi-Agent Routing
Workspaces are how OpenClaw separates different teams, projects, and access levels within a single gateway. Instead of running a different server for each team, you run one gateway and create logical partitions inside it. Each partition โ a workspace โ has its own users, its own agent, and its own configuration.
This chapter covers how workspaces work, how to create and configure them, and how the gateway's router decides which workspace and which agent handles each incoming message.
What a Workspace Is
A workspace is a named container that holds:
- A list of authorized users (their channel IDs)
- An agent configuration (which AI model to use, system prompt, temperature)
- A set of enabled skills
- A channel assignment (which channels feed into this workspace)
- A routing priority when multiple workspaces share a channel
Think of a workspace like a Slack workspace or a GitHub organization โ it defines who has access to what. One OpenClaw gateway can run many workspaces at the same time.
Common Workspace Patterns
| Pattern | Example |
|---|---|
| Team-based | frontend-team, backend-team, devops |
| Project-based | project-alpha, client-acme |
| Role-based | admin, developer, readonly |
| Channel-based | whatsapp-clients, slack-internal |
| Personal | personal workspace for just yourself |
Defining Workspaces in openclaw.json
Workspaces are defined under the workspaces key in ~/.openclaw/openclaw.json.
Minimal workspace:
{
"workspaces": [
{
"id": "personal",
"agent": "claude-sonnet",
"allowlist": ["+1234567890"]
}
]
}
Full workspace with all options:
{
"workspaces": [
{
"id": "team-dev",
"label": "Development Team",
"agent": "claude-opus",
"systemPrompt": "You are an expert software engineer. Always write clean, tested code.",
"temperature": 0.3,
"maxTokens": 8192,
"channels": ["slack", "telegram"],
"allowlist": [
"U01ABCDE123",
"U02FGHIJ456"
],
"skills": ["bash", "files", "web-search", "github"],
"sessionTimeout": 1800
}
]
}
Key fields:
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the workspace |
label | string | Human-readable name (optional) |
agent | string | AI model to use |
systemPrompt | string | Override the default system prompt |
temperature | number | 0.0 (deterministic) to 1.0 (creative) |
maxTokens | number | Maximum response length |
channels | array | Which channels feed into this workspace |
allowlist | array | Channel-specific user IDs who have access |
skills | array | Skills available to the agent in this workspace |
sessionTimeout | number | Seconds of inactivity before session expires |
Multi-Workspace Routing
When a message comes in, the Router matches it to the right workspace using this logic:
1. Which channel did this message come from?
โ Only check workspaces that include this channel
2. Is the sender's channel ID in the allowlist?
โ Match the first workspace where they are listed
3. Is there a wildcard "*" in the allowlist?
โ Match this workspace for any user on this channel
4. No match found?
โ Send the default "unauthorized" response (or silence)
Priority order
When a user is listed in multiple workspaces, the workspace listed first in openclaw.json takes priority. Order matters.
{
"workspaces": [
{
"id": "admin",
"allowlist": ["+1234567890"],
"agent": "claude-opus"
},
{
"id": "general",
"allowlist": ["*"],
"agent": "claude-haiku"
}
]
}
In this example, +1234567890 gets the admin workspace (Claude Opus) because admin is listed first. All other users get the general workspace (Claude Haiku).
Agent Selection
Each workspace points to one agent. Agents are defined separately under the agents key:
{
"agents": {
"claude-opus": {
"provider": "anthropic",
"model": "claude-opus-4-7",
"apiKey": "${ANTHROPIC_API_KEY}"
},
"claude-haiku": {
"provider": "anthropic",
"model": "claude-haiku-4-5-20251001",
"apiKey": "${ANTHROPIC_API_KEY}"
},
"gpt4o": {
"provider": "openai",
"model": "gpt-4o",
"apiKey": "${OPENAI_API_KEY}"
},
"local-llama": {
"provider": "ollama",
"model": "llama3",
"baseUrl": "http://localhost:11434"
}
}
}
Multiple workspaces can share the same agent definition. The agent definition says which model and which API key; the workspace says which users and which system prompt.
System Prompts
Each workspace can have its own system prompt that shapes how the AI behaves:
{
"workspaces": [
{
"id": "client-support",
"systemPrompt": "You are a helpful customer support assistant for Acme Corp. Always be polite, concise, and professional. Do not discuss internal code or infrastructure. If asked about pricing, direct users to the sales team.",
"agent": "claude-sonnet"
},
{
"id": "dev-team",
"systemPrompt": "You are a senior software engineer helping the development team. You have full access to run commands, read files, and write code. Prefer TypeScript and follow our ESLint configuration.",
"agent": "claude-opus"
}
]
}
Workspace-Level Skill Control
Skills are enabled per workspace. This gives you fine-grained control over what each team can do:
{
"workspaces": [
{
"id": "readonly-demo",
"skills": ["web-search"],
"allowlist": ["*"]
},
{
"id": "power-users",
"skills": ["bash", "files", "web-search", "github", "database"],
"allowlist": ["U01ADMIN"]
}
]
}
The readonly-demo workspace only allows web search โ it cannot run commands or read files. The power-users workspace has full access.
Channel Scoping
By default, a workspace accepts messages from all channels. You can restrict it:
{
"workspaces": [
{
"id": "whatsapp-clients",
"channels": ["whatsapp"],
"agent": "claude-haiku",
"allowlist": ["*"]
},
{
"id": "slack-devs",
"channels": ["slack"],
"agent": "claude-opus",
"allowlist": ["U01DEV", "U02DEV"]
}
]
}
Client messages from WhatsApp go to the light claude-haiku model. Developer messages from Slack go to the full-power claude-opus model.
Managing Users in Workspaces
Add a user via pairing
The cleanest way to add users is through the pairing flow (covered in Chapter 13). Users generate a pairing code and their channel ID is automatically added to the correct workspace allowlist.
Add a user manually
Edit openclaw.json and add their channel ID to the allowlist, then restart:
openclaw restart
Remove a user
Remove their channel ID from the allowlist and restart.
Wildcard access
Use "*" in the allowlist to allow any user on the connected channel:
{
"allowlist": ["*"]
}
Use with caution โ only do this on channels that are already private (e.g., a private Slack channel, an invite-only Telegram group).
Viewing Workspace Status
openclaw status
Output:
Gateway: running (uptime 2h 14m)
Workspaces:
team-dev agent=claude-opus users=4 channels=slack,telegram sessions=2
client-demo agent=claude-haiku users=* channels=whatsapp sessions=7
personal agent=claude-sonnet users=1 channels=telegram sessions=0
Best Practices
- Give workspaces meaningful IDs โ
team-frontendis better thanws1 - Use environment variables for API keys โ never hardcode keys in
openclaw.json - Order workspaces by priority โ most specific first, wildcard workspaces last
- Limit skills to what each workspace actually needs โ least privilege
- Set a lower temperature for code-generation workspaces โ 0.1โ0.3 reduces hallucinations
- Use separate agents for cost control โ route simple queries to Claude Haiku, complex ones to Opus
Next: Chapter 6 โ Channels Overview โ A complete map of all channels OpenClaw supports and how they compare.