Use Case
Multi-account WhatsApp setups need the ability to receive and process DMs on one account without responding (shadow mode), while allowing normal DM responses on a different account.
Concrete scenario:
- Primary account receives DMs from contacts. The agent should read, learn from, and process these messages — but never send a reply back to the sender.
- Secondary account receives DMs from the same or different contacts. The agent should respond normally.
- Currently, the only architectural enforcement is
sendPolicy, but it matches on channel and chatType — not on accountId or peer. Denying chatType: "direct" on WhatsApp kills DM responses on both accounts.
Why prompt-level enforcement is insufficient:
SOUL.md instructions like "output NO_REPLY for DMs on the primary account" are probabilistic. In observed production behavior, the model ignored an explicit NO_REPLY rule and responded directly to a DM on the primary account despite clear instructions not to. Prompt-level rules are guidance, not gates.
Current State
session.sendPolicy.rules[].match supports: channel, chatType, keyPrefix, rawKeyPrefix
- WhatsApp session keys do not encode
accountId (e.g. agent:main:whatsapp:direct:+1234567890 is the same structure for both primary and secondary accounts), so rawKeyPrefix cannot distinguish accounts
accountId is stored as session metadata but not exposed to sendPolicy matching
heartbeat.directPolicy: "block" exists but only applies to heartbeat-triggered turns, not message-triggered turns
- No per-binding reply policy or delivery redirect mechanism exists
Proposed Solutions (any of these would work)
Option A: Add accountId to sendPolicy match (preferred)
{
"session": {
"sendPolicy": {
"default": "allow",
"rules": [
{
"action": "deny",
"match": {
"channel": "whatsapp",
"chatType": "direct",
"accountId": "default"
}
}
]
}
}
}
This would suppress outbound delivery for DMs on the default/primary WhatsApp account while allowing DMs on the secondary account. The agent still processes the inbound message (per the 2026.4.12 sendPolicy fix in #65461).
Option B: Add peer to sendPolicy match
{
"action": "deny",
"match": {
"channel": "whatsapp",
"chatType": "direct",
"peer": "+1234567890"
}
}
Per-number send suppression. More granular but more verbose for multi-contact scenarios.
Option C: Per-binding sendPolicy or replyPolicy
{
"bindings": [
{
"type": "route",
"agentId": "main",
"match": { "channel": "whatsapp", "accountId": "default", "peer": { "kind": "direct" } },
"sendPolicy": "deny"
}
]
}
Binding-level delivery control. Cleanest for per-route shadow mode.
Option D: Delivery redirect
Route the agent's reply to a different destination (e.g. a specific group or webchat) instead of back to the original sender. Useful for "draft and forward for approval" workflows.
Environment
- OpenClaw 2026.4.14
- WhatsApp dual-account setup (default + secondary)
- 6 agents with different channel bindings
Related
Use Case
Multi-account WhatsApp setups need the ability to receive and process DMs on one account without responding (shadow mode), while allowing normal DM responses on a different account.
Concrete scenario:
sendPolicy, but it matches onchannelandchatType— not onaccountIdor peer. DenyingchatType: "direct"on WhatsApp kills DM responses on both accounts.Why prompt-level enforcement is insufficient:
SOUL.md instructions like "output NO_REPLY for DMs on the primary account" are probabilistic. In observed production behavior, the model ignored an explicit
NO_REPLYrule and responded directly to a DM on the primary account despite clear instructions not to. Prompt-level rules are guidance, not gates.Current State
session.sendPolicy.rules[].matchsupports:channel,chatType,keyPrefix,rawKeyPrefixaccountId(e.g.agent:main:whatsapp:direct:+1234567890is the same structure for both primary and secondary accounts), sorawKeyPrefixcannot distinguish accountsaccountIdis stored as session metadata but not exposed to sendPolicy matchingheartbeat.directPolicy: "block"exists but only applies to heartbeat-triggered turns, not message-triggered turnsProposed Solutions (any of these would work)
Option A: Add
accountIdto sendPolicy match (preferred){ "session": { "sendPolicy": { "default": "allow", "rules": [ { "action": "deny", "match": { "channel": "whatsapp", "chatType": "direct", "accountId": "default" } } ] } } }This would suppress outbound delivery for DMs on the default/primary WhatsApp account while allowing DMs on the secondary account. The agent still processes the inbound message (per the 2026.4.12 sendPolicy fix in #65461).
Option B: Add
peerto sendPolicy match{ "action": "deny", "match": { "channel": "whatsapp", "chatType": "direct", "peer": "+1234567890" } }Per-number send suppression. More granular but more verbose for multi-contact scenarios.
Option C: Per-binding sendPolicy or replyPolicy
{ "bindings": [ { "type": "route", "agentId": "main", "match": { "channel": "whatsapp", "accountId": "default", "peer": { "kind": "direct" } }, "sendPolicy": "deny" } ] }Binding-level delivery control. Cleanest for per-route shadow mode.
Option D: Delivery redirect
Route the agent's reply to a different destination (e.g. a specific group or webchat) instead of back to the original sender. Useful for "draft and forward for approval" workflows.
Environment
Related