#Make a specific openclaw agent answer on a specific Signal group

1 messages · Page 1 of 1 (latest)

wet tangle
#

Routing an agent to a signal group using bindings doesn't work:
{
"agentId": "secondary",
"match": {
"channel": "signal",
"peer": {
"kind": "group",
"id": "groupidhere"
}
}
}

the main agent answers instead of secondary

upper boughBOT
lunar flameBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

upper boughBOT
lyric compassBOT
#

That binding shape looks right.

If main still answers, the two usual gotchas are:

  1. peer.id must be the exact raw Signal groupId

    • not the group name
    • not signal:group:<id>
    • not an invite/link id
  2. If you have multiple Signal accounts, omitting match.accountId only matches the default account.

    • For a quick test, try:
{
  agentId: "secondary",
  match: {
#
    channel: "signal",
    accountId: "*",
    peer: {
      kind: "group",
      id: "groupidhere"
    }
  }
}

Also, within the same match tier, the first matching binding wins, so a duplicate earlier binding can steal it.

Can you paste these 3 things?

wet tangle
#

how do i get the exact group id

lyric compassBOT
#

Fastest way right now:

  1. send one test message in that Signal group
  2. run:
openclaw sessions --all-agents --active 10 --json

Then look for a session key like:

agent:main:signal:group:<groupId>

That <groupId> is the exact value you want in the binding.

#

Since main is answering right now, you can also narrow it to:

openclaw sessions --agent main --active 10 --json

Then use:

{
  agentId: "secondary",
  match: {
    channel: "signal",
    peer: {
      kind: "group",
      id: "<that exact groupId>"
wet tangle
#

openclaw sessions --all-agents --active 10 --json
Config warnings:\n- plugins.entries.active-memory: plugin disabled (bundled (disabled by default)) but config is present
Config warnings:\n- plugins.entries.active-memory: plugin disabled (bundled (disabled by default)) but config is present
Config warnings:\n- plugins.entries.active-memory: plugin disabled (bundled (disabled by default)) but config is present
Config warnings:\n- plugins.entries.active-memory: plugin disabled (bundled (disabled by default)) but config is present
{
"path": null,
"stores": [
{
"agentId": "main",
"path": "/home/user/.openclaw/agents/main/sessions/sessions.json"
},
{
"agentId": "secondary",
"path": "/home/user/.openclaw/agents/secondary/sessions/sessions.json"
},
{
"agentId": "third",
"path": "/home/user/.openclaw/agents/third/sessions/sessions.json"
}
],
"allAgents": true,
"count": 1,
"activeMinutes": 10,
"sessions": [
{
"key": "agent:main:signal:group:REDACTED",
"updatedAt": 1776682773844,
"ageMs": 50845,
"sessionId": "ca92a363-78c1-4d10-9707-b786c79adec8",
"systemSent": true,
"abortedLastRun": false,
"totalTokens": null,
"totalTokensFresh": false,
"model": "gemma-4-31B",
"modelProvider": "llamacpp",
"contextTokens": 262144,
"agentId": "main", // HERE'S THE ISSUE! IT SHOULD BE SECONDARY
"kind": "group"
}
]
}

check the comment on agentId

lyric compassBOT
#

Yep, I checked it, and that agentId: "main" is the symptom, not the root cause.

If the binding were matching, that session key would be:

agent:secondary:signal:group:REDACTED

So right now the Signal group is still falling through to the default route.

The next thing I need is:

openclaw agents bindings --json

That tells us whether OpenClaw actually loaded your binding.

#

Two likely outcomes:

  1. Your binding is missing from the loaded bindings

    • config path/shape issue
    • hot reload didn’t pick it up
    • typo in agentId or bindings placement
  2. Your binding is present, but still not matching

    • most likely Signal accountId mismatch
    • quick test: add accountId: "*" to that binding

Example test binding:

{
  agentId: "secondary",
#
  match: {
    channel: "signal",
    accountId: "*",
    peer: {
      kind: "group",
      id: "REDACTED"
    }
  }
}

Also, the active-memory warnings are unrelated here.

Paste openclaw agents bindings --json and I’ll tell you exactly what’s off.

wet tangle
#

[
{
"agentId": "main",
"match": {
"channel": "signal",
"peer": {
"kind": "direct",
"id": "MYID"
}
},
"description": "signal peer=direct:MYID"
},
{
"agentId": "secondary",
"match": {
"channel": "signal",
"peer": {
"kind": "group",
"id": "REDACTED"
}
},
"description": "signal peer=group:REDACTED"
},
{
"agentId": "third",
"match": {
"channel": "signal",
"peer": {
"kind": "direct",
"id": "FRIENDID"
}
},
"description": "signal peer=direct:FRIENDID"
},
]

but when I look at sessions, main still handles it:

agent:main:signal:group:REDACTED

lyric compassBOT
#

Nice, this narrows it down a lot.

Your main binding is peer.kind: "direct", so it should not steal a Signal group message. That means the secondary binding is loaded, but the runtime still isn’t matching it.

The two most likely causes now are:

  1. Signal account scope

    • omitted accountId only matches the default Signal account
    • if that group is arriving on a different Signal account, it falls through to main
  2. stale runtime state

    • config is loaded, but the gateway is still routing with old state

I’d try this exact test:

#
{
  agentId: "secondary",
  match: {
    channel: "signal",
    accountId: "*",
    peer: {
      kind: "group",
      id: "REDACTED"
    }
  }
}

Then restart once:

#
openclaw gateway restart

Then send one new message in that Signal group and rerun:

openclaw sessions --all-agents --active 10 --json

If it still comes out as:

agent:main:signal:group:REDACTED

then I’d call this a likely bug, not config error.

wet tangle
#

how do i init that agent in TUI first?

lyric compassBOT
#

You don’t need to init it first for routing to work, but if you want to bootstrap secondary manually first, do this:

openclaw tui

Then in the TUI:

/agent secondary

or press Ctrl+G and pick secondary.

Then send any message, even just:

#
hi

That first run will bootstrap the agent workspace.

If secondary does not exist yet, create it first:

openclaw agents add secondary

That launches the agent onboarding flow. After that, go back to TUI and switch with:

/agent secondary
wet tangle
#

do i need two different signal accounts?

lyric compassBOT
#

No, not for this.

One Signal account/number can still route:

  • one group -> secondary
  • another group -> main
  • specific DMs -> other agents

So for multi-agent routing, you do not need two Signal accounts.

You’d only want multiple Signal accounts if you need:

  • different outbound phone numbers / identities
  • different per-account Signal config
  • separate bot accounts for different use cases

One important nuance from the Signal docs: if you run the bot on your personal Signal account, it ignores your own messages for loop protection. For “I message the bot and it replies,” a separate bot number is recommended.

wet tangle
#

how to set the default openclaw agent?

lyric compassBOT
#

Set default: true on the agent you want in agents.list.

Example:

{
  agents: {
    list: [
      {
        id: "secondary",
        default: true,
        workspace: "~/.openclaw/workspace-secondary"
      },
      {
        id: "main",
        workspace: "~/.openclaw/workspace"
#
      },
      {
        id: "third",
        workspace: "~/.openclaw/workspace-third"
      }
    ]
  }
}

A couple of gotchas:

  • only one agent should have default: true
  • if none has it, OpenClaw uses the first entry in agents.list
  • this affects fallback routing and commands/TUI sessions where no explicit agent is selected
wet tangle
#

even after setting secondary as the default and the binding to the signal group... main still answers the group

lyric compassBOT
#

Yeah, that’s the big tell.

An exact peer binding should beat the default agent every time. And if you even changed the default to secondary but live Signal traffic still lands on main, then this is no longer a normal config mistake.

The two things I’d check next are:

  1. the daemon is using a different config/profile than the CLI
  2. the gateway runtime is stale or there’s a bug

Please run these and paste the output:

openclaw --version
openclaw gateway status
openclaw status --all
#

Then do one restart:

openclaw gateway restart

and test the Signal group again.

Why these matter:

  • openclaw gateway status shows which config the running service is likely using
  • if that path/profile is different from the file you edited, that explains everything
  • if it’s the same config and a restart still routes to main, I’d treat it as a likely bug

At that point, please file a GitHub issue with:

  • openclaw --version
wet tangle
#

./agents outputs:

agents:

(none)

lyric compassBOT
#

If you mean /agents in the TUI, then agents: (none) is a huge clue.

That does not line up with the earlier CLI output where you had:

  • main
  • secondary
  • third

So I’m pretty confident now that your CLI and the running TUI/gateway are not looking at the same config/profile/runtime.

In other words, you likely edited one OpenClaw config, but the live gateway that Signal is hitting is using another one.

Please run these from the same shell/profile you use to start TUI:

openclaw --version
#
openclaw gateway status
openclaw agents list --bindings

If you use profiles, make sure you use the same one everywhere, for example:

openclaw --profile myprofile gateway status
openclaw --profile myprofile tui
openclaw --profile myprofile agents list --bindings

Also, if your TUI is connected to a remote gateway, the agents/workspaces live on the gateway host, not your local machine.

So yes, agents: (none) strongly suggests wrong gateway / wrong profile / wrong config path, not a bad Signal binding.

wet tangle
#

now openclaw ignores me in a group

lyric compassBOT
#

Ah, that’s a different layer than routing.

bindings choose which agent should answer.
But Signal group messages still have to pass group access + mention gating first.

For Signal, the common reasons it ignores a group are:

  1. channels.signal.groupPolicy is still allowlist
  2. channels.signal.groupAllowFrom does not include you
  3. requireMention is on, but Signal has no native mentions, so you need text mentionPatterns

Quickest permissive test:

{
  channels: {
#
    signal: {
      groupPolicy: "open",
      groups: {
        "*": { requireMention: false }
      }
    }
  }
}

Then restart:

openclaw gateway restart
#

If that makes it reply, then the problem is definitely group gating, not bindings.

Safer version for just your group:

{
  channels: {
    signal: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["+1555YOURNUMBER"],
      groups: {
        "REDACTED": { requireMention: false }
      }
    }
  }
}
#

If you do want mention-only behavior on Signal, you need mention patterns, for example:

{
  agents: {
    list: [
      {
        id: "secondary",
        groupChat: { mentionPatterns: ["openclaw", "@openclaw"] }
      }
    ]
  }
}

Important nuance: DM pairing does not unlock group triggers. Groups still need groupPolicy / groupAllowFrom.