Access Groups: Reusable Sender Allowlists for Channels

Learn how to define named sender groups with accessGroups and reference them in channel allowlists using accessGroup:<name>. Ideal for managing consistent sender permissions across multiple messaging channels.

Read this when

  • Configuring the same allowlist across multiple message channels
  • Sharing DM and group sender access rules
  • Reviewing message-channel access control

Access groups are named collections of senders that you set up once using accessGroups, then pull into channel allowlists through accessGroup:<name>.

They come in handy when the same individuals need access to multiple messaging channels, or when a single trusted set should govern both DM and group sender authorization.

Defining a group by itself grants nothing. It only takes effect when an allowlist field points to it.

Static message sender groups

Static sender groups rely on type: "message.senders". The members structure is organized by message-channel id, with "*" covering entries that apply across all channels:

{
  accessGroups: {
    operators: {
      type: "message.senders",
      members: {
        "*": ["global-owner-id"],
        discord: ["discord:123456789012345678"],
        telegram: ["987654321"],
        whatsapp: ["+15551234567"],
      },
    },
  },
}
KeyMeaning
"*"Entries that apply to every message channel referencing the group.
discord, telegram, ...Entries that apply only to that channel's allowlist matching.

Matching follows the destination channel's standard allowFrom rules. OpenClaw does not map sender ids between channels: if Alice has separate Telegram and Discord ids, both need to appear under the appropriate channel keys.

Reference groups from allowlists

You can reference a group with accessGroup:<name> at any point where the message channel path supports sender allowlists.

DM allowlist example:

{
  accessGroups: {
    operators: {
      type: "message.senders",
      members: {
        discord: ["discord:123456789012345678"],
        telegram: ["987654321"],
      },
    },
  },
  channels: {
    discord: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:operators"],
    },
    telegram: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:operators"],
    },
  },
}

Group sender allowlist example:

{
  accessGroups: {
    oncall: {
      type: "message.senders",
      members: {
        whatsapp: ["+15551234567"],
        googlechat: ["users/1234567890"],
      },
    },
  },
  channels: {
    whatsapp: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["accessGroup:oncall"],
    },
    googlechat: {
      groups: {
        "spaces/AAA": {
          users: ["accessGroup:oncall"],
        },
      },
    },
  },
}

Groups and direct entries can be combined:

{
  channels: {
    discord: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:operators", "discord:123456789012345678"],
    },
  },
}

Supported message-channel paths

Access groups function within the shared message-channel authorization paths:

  • DM sender allowlists like channels.<channel>.allowFrom
  • group sender allowlists like channels.<channel>.groupAllowFrom
  • channel-specific per-room sender allowlists that follow the same sender matching rules (for instance Google Chat groups.<space>.users)
  • command authorization paths that reuse message-channel sender allowlists

Whether a channel supports this depends on its wiring through the shared OpenClaw sender-authorization helpers. The currently supported channel integrations are ClickClack, Discord, Feishu, Google Chat, iMessage, IRC, LINE, Mattermost, Microsoft Teams, Nextcloud Talk, Nostr, QQ Bot, Signal, Slack, SMS, Telegram, WhatsApp, Zalo, and Zalo Personal. Static message.senders groups are channel-agnostic, so new message channels pick them up automatically by relying on the shared plugin SDK ingress helpers rather than custom allowlist expansion.

Discord channel audiences

Discord offers an additional dynamic access group type:

{
  accessGroups: {
    maintainers: {
      type: "discord.channelAudience",
      guildId: "1456350064065904867",
      channelId: "1456744319972282449",
      membership: "canViewChannel",
    },
  },
  channels: {
    discord: {
      dmPolicy: "allowlist",
      allowFrom: ["accessGroup:maintainers"],
    },
  },
}

discord.channelAudience translates to "allow Discord DM senders who can currently view this guild channel." At authorization time, OpenClaw resolves the sender through Discord and applies Discord ViewChannel permission rules. membership is optional, defaulting to canViewChannel.

This fits when a Discord channel already serves as the source of truth for a team, such as #maintainers or #on-call.

Requirements and failure behavior:

  • The bot must have access to the guild and channel.
  • The Discord Developer Portal Server Members Intent must be enabled for the bot.
  • The access group fails closed when Discord returns Missing Access, the sender cannot be resolved as a guild member, or the channel belongs to another guild.

Additional Discord-specific examples: Discord access control

Plugin diagnostics

Plugin authors can read structured access-group state without flattening it back into an allowlist:

import { resolveAccessGroupAllowFromState } from "openclaw/plugin-sdk/access-groups";

const state = await resolveAccessGroupAllowFromState({
  accessGroups: cfg.accessGroups,
  allowFrom: channelConfig.allowFrom,
  channel: "my-channel",
  accountId: "default",
  senderId,
  isSenderAllowed,
});

The result reports which groups are referenced, matched, missing, unsupported, and failed. This is useful for diagnostics or conformance tests. Use expandAllowFromWithAccessGroups(...) only for compatibility paths that still expect a flat allowFrom array.

Security notes

  • Access groups act as allowlist aliases, not roles. On their own, they do not create owners, approve pairing requests, or grant tool permissions.
  • dmPolicy: "open" still requires "*" in the effective DM allowlist. Referencing an access group does not equal public access.
  • Missing group names fail closed. If allowFrom contains accessGroup:operators and accessGroups.operators is absent, that entry authorizes no one.
  • Keep channel ids stable. When a channel supports both, prefer numeric/user ids over display names.

Troubleshooting

If a sender should match but is blocked:

  1. Verify the allowlist field holds the exact accessGroup:<name> reference.
  2. Verify accessGroups.<name>.type is correct.
  3. Verify the sender id appears under the matching channel key, or under "*".
  4. Verify the entry uses that channel's normal allowlist syntax.
  5. For Discord channel audiences, verify the bot can see the guild channel and has Server Members Intent enabled.

Run openclaw doctor after editing access-control config. It catches many invalid allowlist and policy combinations before runtime.

842 words · updated Aug 1, 2026