Openclaw Message Routing Blueprint
Central message router resolving inbound messages to agents via binding precedence, role-based routing, and guild/channel/peer matching
| Feature | openclaw-message-routing |
| Category | Integration |
| Version | 1.0.0 |
| Tags | gateway, routing, messaging, multi-tenant, binding-resolution |
| YAML Source | View on GitHub |
| JSON API | openclaw-message-routing.json |
Actors
| ID | Name | Type | Description |
|---|---|---|---|
message_source | Inbound Message Source | external | |
gateway | OpenClaw Gateway | system | |
agent | AI Agent | system | |
config_store | Configuration Store | system |
Fields
| Name | Type | Required | Label | Description |
|---|---|---|---|---|
channel | text | Yes | Messaging Channel | |
account_id | text | No | Account ID | |
peer_kind | select | No | Peer Type | |
peer_id | text | No | Peer ID | |
guild_id | text | No | Guild ID | |
team_id | text | No | Team ID | |
member_role_ids | json | No | Member Roles | |
agent_id | text | Yes | Resolved Agent ID | |
session_key | text | Yes | Session Key | |
main_session_key | text | Yes | Main Session Key | |
last_route_policy | select | Yes | Last Route Policy | |
matched_by | select | Yes | Binding Match Type |
States
State field: undefined
Rules
- routing:
-
binding_precedence: Bindings evaluated in strict order (first match wins): 1. binding.peer — exact direct peer (highest priority) 2. binding.peer.parent — thread parent peer 3. binding.peer.wildcard — wildcard peer kind (group ↔ channel) 4. binding.guild+roles — guild + role intersection 5. binding.guild — guild ID match 6. binding.team — team ID match 7. binding.account — account-level default 8. binding.channel — channel-level default 9. default — system default agent (lowest priority)
-
agent_id_normalization: All agent IDs normalized to lowercase for matching. Case-insensitive lookup preserves original ID casing in resolution. Missing/empty agent_id falls back to resolveDefaultAgentId(cfg).
-
session_key_derivation: Format: agent:
: Examples:
-
- agent:mybot:main (DM collapse)
- agent:mybot:discord:direct:userid (per-channel DM)
- agent:mybot:group:groupid (group messages)
-
agent:mybot:discord:direct:userid:thread:threadid (threaded) Max length: 255 characters Normalized to lowercase for storage
- cache_strategy: Resolved routes cached with per-cfg object weak map. Cache limit: 4000 entries (entire cache cleared on limit exceeded). Stale detection: config change clears all caches for that object.
- binding_matching:
- peer_normalization: Peer ID normalization:
- String: trimmed directly
- Number/BigInt: converted to string
-
Null/undefined: treated as “unknown” Wildcard: “group” matches “channel” (platforms differ)
-
role_matching: Discord role-based routing via set intersection. All specified roles required to match (AND logic). Empty roles[] matches any member (no restriction).
-
guild_team_scoping: guildId/teamId matching bypasses channel scope. Sub-guild bindings override guild bindings.
-
- dm_policy:
-
peer_kind_matching: peer.kind values: direct (1:1), group (multi-user), channel (public) Peer.id normalization: trimmed, ID-only (remove prefixes) Parent peer matching for threads: inheritance of parent policy
-
last_route_policy_logic: lastRoutePolicy = (sessionKey === mainSessionKey) ? “main” : “session” “main”: all inbound from peer update mainSessionKey (collapsed) “session”: updates target specific sessionKey (per-peer context)
-
SLA
| Scope | Max Duration | Escalation |
|---|---|---|
| routing_latency | 100ms | |
| cache_hit_ratio |
Outcomes
Binding_matched (Priority: 1)
Given:
- message received on channel
- channel and account_id provided
matched_by(computed) neqdefault
Then:
- set_field target:
agent_idvalue:resolved agent from binding - set_field target:
session_keyvalue:derived from agentId, channel, peer, dmScope - set_field target:
matched_byvalue:binding rule that matched - emit_event event:
route.resolved
Result: Agent determined, route resolved to session_key
Fallback_to_default (Priority: 10)
Given:
- message processed
- no binding matched any rule
Then:
- set_field target:
agent_idvalue:resolveDefaultAgentId(cfg) - set_field target:
matched_byvalue:default - emit_event event:
route.fallback
Result: Fallback agent used, message proceeds to dispatch
Errors
| Code | Status | Message | Retry |
|---|---|---|---|
AGENT_NOT_FOUND | 404 | Agent not found in configuration | No |
INVALID_SESSION_KEY | 400 | Invalid session key format | No |
BINDING_RESOLUTION_FAILED | 500 | Unable to resolve agent binding | No |
CACHE_MISS | 500 | Route cache miss | No |
Events
| Event | Description | Payload |
|---|---|---|
route.resolved | agent_id, channel, matched_by, session_key, last_route_policy | |
route.fallback | channel, account_id, peer_kind, default_agent_id | |
route.cache_cleared | reason, cache_size_before |
Related Blueprints
| Feature | Relationship | Reason |
|---|---|---|
| openclaw-session-management | required | Session key used in persistent conversation store |
| openclaw-gateway-authentication | required | Auth must resolve before routing to determine user scope |
AGI Readiness
Goals
Reliable Openclaw Message Routing
Central message router resolving inbound messages to agents via binding precedence, role-based routing, and guild/channel/peer matching
Success Metrics:
| Metric | Target | Measurement |
|---|---|---|
| success_rate | >= 99.5% | Successful operations divided by total attempts |
| error_recovery_rate | >= 95% | Errors that auto-recover without manual intervention |
Constraints:
- availability (non-negotiable): Must degrade gracefully when dependencies are unavailable
Autonomy
Level: supervised
Escalation Triggers:
error_rate > 5
Tradeoffs
| Prefer | Over | Reason |
|---|---|---|
| reliability | throughput | integration failures can cascade across systems |
Coordination
Protocol: orchestrated
Consumes:
| Capability | From | Fallback |
|---|---|---|
openclaw_session_management | openclaw-session-management | degrade |
openclaw_gateway_authentication | openclaw-gateway-authentication | degrade |
Safety
| Action | Permission | Cooldown | Max Auto |
|---|---|---|---|
| binding_matched | autonomous | - | - |
| fallback_to_default | autonomous | - | - |