Mainstream messaging

iMessage access control and routing

Who is admitted, how messages route to sessions, and which chats can write config.

Access control and routing

DM policy

channels.imessage.dmPolicy controls direct messages:

  • pairing (default)
  • allowlist (requires at least one allowFrom entry)
  • open (requires allowFrom to include "*")
  • disabled

Allowlist field: channels.imessage.allowFrom.

Allowlist entries must identify senders: handles or static sender access groups (accessGroup:<name>). Use channels.imessage.groupAllowFrom for chat targets such as chat_id:*, chat_guid:*, or chat_identifier:*; use channels.imessage.groups for numeric chat_id registry keys.

Group policy + mentions

channels.imessage.groupPolicy controls group handling:

  • allowlist (default)
  • open
  • disabled

Group sender allowlist: channels.imessage.groupAllowFrom.

groupAllowFrom entries can also reference static sender access groups (accessGroup:<name>).

Runtime fallback: if groupAllowFrom is unset, iMessage group sender checks use allowFrom; set groupAllowFrom when DM and group admission should differ. An explicitly empty groupAllowFrom: [] does not fall back — it blocks all group senders under allowlist. Runtime note: if channels.imessage is completely missing, runtime falls back to groupPolicy="allowlist" and logs a warning (even if channels.defaults.groupPolicy is set).

Mention gating for groups:

  • iMessage has no native mention metadata
  • mention detection uses agents.entries.*.groupChat.mentionPatterns, then messages.groupChat.mentionPatterns; when neither is set, patterns are derived from the routed agent's identity.name and identity.emoji
  • groups require a mention by default, even when no patterns were explicitly configured; an allowlisted sender's message can therefore be skipped unless it contains the agent's name or emoji
  • an explicit mentionPatterns: [] at the selected agent or global level suppresses identity-derived patterns; iMessage cannot enforce mention gating when no usable patterns remain
  • control commands from authorized senders bypass mention gating

To process every message from allowed senders in one group, set that chat's requireMention to false:

json5
{  channels: {    imessage: {      groupPolicy: "allowlist",      groupAllowFrom: ["+15555550123", "+15555550124"],      groups: {        "*": {},        "123": { requireMention: false },      },    },  },}

Replace 123 with the numeric chat ID from imsg chats --limit 20 --json. Edit the map already supplying that account's group policy: channels.imessage.groups, or channels.imessage.accounts.<account-id>.groups when it overrides the root map. This also applies to accounts.default.groups; merely having an account entry does not mean its own groups map is needed. An empty account map inherits the root map only when at most one account is configured.

Preserve the existing wildcard and every per-group setting, changing only the target chat's requireMention. Account maps replace the whole inherited map, so if you intentionally create an account-specific override, first copy the complete inherited map, including all wildcard and per-group policies. When no map previously applied, "*": {} preserves admission to other groups while keeping their default mention requirement. Keep a restricted map restricted. groupAllowFrom still controls sender access.

A skipped message with no mention produces a warning at the default log level with the chat ID and the requireMention: false fix. Repeated warnings for the same chat are suppressed by a bounded in-memory cache; restarting the channel or evicting a cache entry allows the warning again.

Per-group systemPrompt:

Each entry under channels.imessage.groups.* accepts an optional systemPrompt string, injected into the agent's system prompt on every turn that handles a message in that group. Resolution mirrors channels.whatsapp.groups:

  1. Group-specific system prompt (groups["<chat_id>"].systemPrompt): used when the specific group entry exists in the map and its systemPrompt key is defined. If systemPrompt is an empty string ("") the wildcard is suppressed and no system prompt is applied to that group.
  2. Group wildcard system prompt (groups["*"].systemPrompt): used when the specific group entry is absent from the map entirely, or when it exists but defines no systemPrompt key.
json5
{  channels: {    imessage: {      groupPolicy: "allowlist",      groupAllowFrom: ["+15555550123"],      groups: {        "*": { systemPrompt: "Use British spelling." },        "8421": {          requireMention: true,          systemPrompt: "This is the on-call rotation chat. Keep replies under 3 sentences.",        },        "9907": {          // explicit suppression: the wildcard "Use British spelling." does not apply here          systemPrompt: "",        },      },    },  },}

Per-group prompts only apply to group messages — direct messages are unaffected.

Sessions and deterministic replies

  • DMs use direct routing; groups use group routing.
  • With default session.dmScope=main, iMessage DMs collapse into the agent main session.
  • Group sessions are isolated (agent:<agentId>:imessage:group:<chat_id>).
  • Replies route back to iMessage using originating channel/target metadata.

Group-ish thread behavior:

Some multi-participant iMessage threads can arrive with is_group=false. If that chat_id is explicitly configured under channels.imessage.groups, OpenClaw treats it as group traffic (group gating + group session isolation).

ACP conversation bindings

iMessage chats can be bound to ACP sessions.

Fast operator flow:

  • Run /acp spawn codex --bind here inside the DM or allowed group chat.
  • Future messages in that same iMessage conversation route to the spawned ACP session.
  • /new and /reset reset the same bound ACP session in place.
  • /acp close closes the ACP session and removes the binding.

Configured persistent bindings use top-level bindings[] entries with type: "acp" and match.channel: "imessage".

match.peer.id can use:

  • normalized DM handle such as +15555550123 or user@example.com
  • chat_id:<id> (recommended for stable group bindings)
  • chat_guid:<guid>
  • chat_identifier:<identifier>

Example:

json5
{  agents: {    entries: {      codex: {        default: true,        runtime: {          type: "acp",          acp: { agent: "codex", backend: "acpx", mode: "persistent" },        },      },    },  },  bindings: [    {      type: "acp",      agentId: "codex",      match: {        channel: "imessage",        accountId: "default",        peer: { kind: "group", id: "chat_id:123" },      },      acp: { label: "codex-group" },    },  ],}

See ACP Agents for shared ACP binding behavior.

Config writes

iMessage allows channel-initiated config writes by default (for /config set|unset when commands.config: true).

Disable:

json5
{  channels: {    imessage: {      configWrites: false,    },  },}
Was this useful?
On this page

On this page