Docs / channels / imessage

iMessage

iMessage (imsg)

Quick setup (beginner)

  1. Ensure Messages is signed in on this Mac.
  2. Install imsg:
    • brew install steipete/tap/imsg
  3. brew install steipete/tap/imsg
  4. Configure OpenClaw with channels.imessage.cliPath and channels.imessage.dbPath.
  5. Start the gateway and approve any macOS prompts (Automation + Full Disk Access).
  • brew install steipete/tap/imsg
text
{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "/usr/local/bin/imsg",
      dbPath: "/Users/<you>/Library/Messages/chat.db",
    },
  },
}

What it is

  • iMessage channel backed by imsg on macOS.
  • Deterministic routing: replies always go back to iMessage.
  • DMs share the agent’s main session; groups are isolated (agent:<agentId>:imessage:group:<chat_id>).
  • If a multi-participant thread arrives with is_group=false, you can still isolate it by chat_id using channels.imessage.groups (see “Group-ish threads” below).

Config writes

text
{
  channels: { imessage: { configWrites: false } },
}

Requirements

  • macOS with Messages signed in.
  • Full Disk Access for OpenClaw + imsg (Messages DB access).
  • Automation permission when sending.
  • channels.imessage.cliPath can point to any command that proxies stdin/stdout (for example, a wrapper script that SSHes to another Mac and runs imsg rpc).

Setup (fast path)

  1. Ensure Messages is signed in on this Mac.
  2. Configure iMessage and start the gateway.

Dedicated bot macOS user (for isolated identity)

  1. Create a dedicated Apple ID (example: my-cool-bot@icloud.com).
    • Apple may require a phone number for verification / 2FA.
  2. Apple may require a phone number for verification / 2FA.
  3. Create a macOS user (example: openclawhome) and sign into it.
  4. Open Messages in that macOS user and sign into iMessage using the bot Apple ID.
  5. Enable Remote Login (System Settings → General → Sharing → Remote Login).
  6. Install imsg:
    • brew install steipete/tap/imsg
  7. brew install steipete/tap/imsg
  8. Set up SSH so ssh <bot-macos-user>@localhost true works without a password.
  9. Point channels.imessage.accounts.bot.cliPath at an SSH wrapper that runs imsg as the bot user.
  • Apple may require a phone number for verification / 2FA.
  • brew install steipete/tap/imsg
text
#!/usr/bin/env bash
set -euo pipefail

# Run an interactive SSH once first to accept host keys:
#   ssh <bot-macos-user>@localhost true
exec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T <bot-macos-user>@localhost \
  "/usr/local/bin/imsg" "$@"
text
{
  channels: {
    imessage: {
      enabled: true,
      accounts: {
        bot: {
          name: "Bot",
          enabled: true,
          cliPath: "/path/to/imsg-bot",
          dbPath: "/Users/<bot-macos-user>/Library/Messages/chat.db",
        },
      },
    },
  },
}

Remote/SSH variant (optional)

text
#!/usr/bin/env bash
exec ssh -T gateway-host imsg "$@"
text
{
  channels: {
    imessage: {
      cliPath: "~/imsg-ssh", // SSH wrapper to remote Mac
      remoteHost: "user@gateway-host", // for SCP file transfer
      includeAttachments: true,
    },
  },
}

Remote Mac via Tailscale (example)

text
┌──────────────────────────────┐          SSH (imsg rpc)          ┌──────────────────────────┐
│ Gateway host (Linux/VM)      │──────────────────────────────────▶│ Mac with Messages + imsg │
│ - openclaw gateway           │          SCP (attachments)        │ - Messages signed in     │
│ - channels.imessage.cliPath  │◀──────────────────────────────────│ - Remote Login enabled   │
└──────────────────────────────┘                                   └──────────────────────────┘
              ▲
              │ Tailscale tailnet (hostname or 100.x.y.z)
              ▼
        user@gateway-host
text
{
  channels: {
    imessage: {
      enabled: true,
      cliPath: "~/.openclaw/scripts/imsg-ssh",
      remoteHost: "bot@mac-mini.tailnet-1234.ts.net",
      includeAttachments: true,
      dbPath: "/Users/bot/Library/Messages/chat.db",
    },
  },
}
text
#!/usr/bin/env bash
exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"
  • Ensure the Mac is signed in to Messages, and Remote Login is enabled.
  • Use SSH keys so ssh bot@mac-mini.tailnet-1234.ts.net works without prompts.
  • remoteHost should match the SSH target so SCP can fetch attachments.

Access control (DMs + groups)

  • Default: channels.imessage.dmPolicy = "pairing".
  • Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
  • Approve via:
    • openclaw pairing list imessage
    • openclaw pairing approve imessage <CODE>
  • openclaw pairing list imessage
  • openclaw pairing approve imessage <CODE>
  • Pairing is the default token exchange for iMessage DMs. Details: Pairing
  • openclaw pairing list imessage
  • openclaw pairing approve imessage <CODE>
  • channels.imessage.groupPolicy = open | allowlist | disabled.
  • channels.imessage.groupAllowFrom controls who can trigger in groups when allowlist is set.
  • Mention gating uses agents.list[].groupChat.mentionPatterns (or messages.groupChat.mentionPatterns) because iMessage has no native mention metadata.
  • Multi-agent override: set per-agent patterns on agents.list[].groupChat.mentionPatterns.

How it works (behavior)

  • imsg streams message events; the gateway normalizes them into the shared channel envelope.
  • Replies always route back to the same chat id or handle.

Group-ish threads (is_group=false)

  • session isolation (separate agent:<agentId>:imessage:group:<chat_id> session key)
  • group allowlisting / mention gating behavior
text
{
  channels: {
    imessage: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["+15555550123"],
      groups: {
        "42": { requireMention: false },
      },
    },
  },
}

Media + limits

  • Optional attachment ingestion via channels.imessage.includeAttachments.
  • Media cap via channels.imessage.mediaMaxMb.

Limits

  • Outbound text is chunked to channels.imessage.textChunkLimit (default 4000).
  • Optional newline chunking: set channels.imessage.chunkMode="newline" to split on blank lines (paragraph boundaries) before length chunking.
  • Media uploads are capped by channels.imessage.mediaMaxMb (default 16).

Addressing / delivery targets

  • chat_id:123 (preferred)
  • chat_guid:...
  • chat_identifier:...
  • direct handles: imessage:+1555 / sms:+1555 / user@example.com
text
imsg chats --limit 20

Configuration reference (iMessage)

  • channels.imessage.enabled: enable/disable channel startup.
  • channels.imessage.cliPath: path to imsg.
  • channels.imessage.dbPath: Messages DB path.
  • channels.imessage.remoteHost: SSH host for SCP attachment transfer when cliPath points to a remote Mac (e.g., user@gateway-host). Auto-detected from SSH wrapper if not set.
  • channels.imessage.service: imessage | sms | auto.
  • channels.imessage.region: SMS region.
  • channels.imessage.dmPolicy: pairing | allowlist | open | disabled (default: pairing).
  • channels.imessage.allowFrom: DM allowlist (handles, emails, E.164 numbers, or chat_id:*). open requires "*". iMessage has no usernames; use handles or chat targets.
  • channels.imessage.groupPolicy: open | allowlist | disabled (default: allowlist).
  • channels.imessage.groupAllowFrom: group sender allowlist.
  • channels.imessage.historyLimit / channels.imessage.accounts.*.historyLimit: max group messages to include as context (0 disables).
  • channels.imessage.dmHistoryLimit: DM history limit in user turns. Per-user overrides: channels.imessage.dms["<handle>"].historyLimit.
  • channels.imessage.groups: per-group defaults + allowlist (use "*" for global defaults).
  • channels.imessage.includeAttachments: ingest attachments into context.
  • channels.imessage.mediaMaxMb: inbound/outbound media cap (MB).
  • channels.imessage.textChunkLimit: outbound chunk size (chars).
  • channels.imessage.chunkMode: length (default) or newline to split on blank lines (paragraph boundaries) before length chunking.
  • agents.list[].groupChat.mentionPatterns (or messages.groupChat.mentionPatterns).
  • messages.responsePrefix.