If you landed here, you probably Googled something like “how do I connect OpenClaw to iMessage?” and immediately discovered that Apple still refuses to ship an official API. The workaround most teams use is BlueBubbles, an open-source bridge that exposes iMessage over a local HTTP/WebSocket interface. In this guide I’ll walk through the exact steps I used to put BlueBubbles on a spare Mac mini and then wire it into an OpenClaw agent running on ClawCloud. Along the way I’ll point out every permission dialog, obscure checkbox, and rate-limit that bit me so you don’t repeat my mistakes.

Why BlueBubbles over the legacy AppleScript bridge?

OpenClaw ships two ways to talk to iMessage:

  • BlueBubbles channel (recommended): Uses the BlueBubbles server to translate iMessage into a simple JSON API. Pros: bi-directional, push notifications, works over the network, attachment support, better performance under heavy load. Cons: you need a Mac that stays logged in 24/7 and a bit more setup.
  • Legacy AppleScript channel: Bundled in @openclaw/channel-imessage-legacy. Pros: zero external dependencies, fine for one-off personal use when OpenClaw already runs on the same Mac. Cons: flaky under macOS Sonoma, outbound only (no real-time inbound hooks), no attachments, breaks whenever Apple renames UI elements.

If you’re automating support chats, group text digests, or anything requiring attachments, use BlueBubbles. The legacy channel is okay for "notify me when mom texts" type experiments.

Prerequisites

  • Mac running macOS 12.6 (Monterey) or later. I tested on 14.3 (Sonoma).
  • Same Mac must be logged into an Apple ID with iMessage enabled.
  • Disable FileVault auto-lock or install bluebubbles-helper so the server can unlock on boot. iMessage refuses to sync when the user session is locked.
  • brew installed (needed for a couple of helper binaries).
  • OpenClaw daemon 2024.5.2 or newer. Make sure you’re on Node.js 22+.
  • ngrok or Cloudflare Tunnel if you want external HTTPS access (optional).

Step 1 — Install BlueBubbles Server

1.1 Download the latest DMG

Grab the signed release from GitHub: BlueBubbles-Server v1.9.0 (checksum faaad2…). Mount and drag it to /Applications.

1.2 Grant the scary macOS permissions

  • Open System Settings → Privacy & Security → Automation. Allow BlueBubbles to control Messages.
  • Under Full Disk Access add both BlueBubbles Server.app and bluebubbles-helper.
  • When macOS prompts for “Accessibility” access, click “Allow”. Without it, AppleScript cannot press the invisible send button.

1.3 Run the initial setup wizard

The wizard asks five things:

  1. iMessage account. Leave default.
  2. Server password. Pick something strong; you’ll need it in OpenClaw config.
  3. Enable “Auto Start” at login. Saves headaches after a power outage.
  4. Choose “Firebase Cloud Messaging” if you want mobile push. I skipped it because OpenClaw polls the WebSocket anyway.
  5. Port selection. Default HTTP 12345 and WebSocket 12346 are fine unless you already use them.

1.4 Verify the server is live

Open a terminal on the Mac and curl the health endpoint:

$ curl http://localhost:12345/api/v1/ping {"status":"ok","timestamp":1715530507}

If you see {"status":"ok"} you’re good. If not, check Console.app for quarantine or TCC errors.

Step 2 — Expose BlueBubbles to ClawCloud

ClawCloud needs to reach your Mac. You have three options:

  1. Tunnel via ngrok:
    $ ngrok http 12345
    Copy the generated HTTPS URL.
  2. WireGuard site-to-site: My preferred approach for production. Install tailscale on the Mac and the ClawCloud VM, then use the Tailscale IP (e.g., 100.94.0.5).
  3. Static IP + NAT: Forward port 12345 on your router and add a DNS A record. Works but you’ll fight CGNAT on many ISPs.

Regardless, make sure ports 12345 (HTTP/REST) and 12346 (WebSocket) are reachable.

Step 3 — Add the BlueBubbles channel in OpenClaw

3.1 Install the channel package

$ npm install @openclaw/channel-imessage-bluebubbles --save

This pulls version 0.6.3 which fixes the WebSocket reconnect bug several users reported in GitHub issue #872.

3.2 Create the channel config

Open your agent’s claw.config.json (or use the web UI if you’re on ClawCloud). Add a new entry:

"channels": [ { "id": "personal-imessage", "type": "imessage-bluebubbles", "displayName": "iMessage (BlueBubbles)", "config": { "serverUrl": "https://bluebubble-demo.ngrok.io", // or Tailscale IP "password": "env:BB_PASSWORD", // reference an env var "listenWebsocket": true, "pollIntervalSec": 10, "allowedChats": ["+15551234567", "family-group-uuid"] // optional whitelist } } ]

Important details:

  • serverUrl must include the scheme. BlueBubbles rejects plain HTTP from non-localhost origins unless you tick “Insecure Local” in its settings.
  • Use env: prefix to avoid committing secrets. In your systemd unit or .env file add BB_PASSWORD="mypassword".
  • If you omit allowedChats, OpenClaw will reply to any incoming thread. Lock it down in production.

3.3 Restart the daemon

$ npx openclaw restart

Watch the logs for:

[imessage-bluebubbles] Connected to BlueBubbles 1.9.0 (build 92) [imessage-bluebubbles] Subscribed to 2 listeners

If you see 401 Unauthorized, the password or server URL is wrong.

Step 4 — Test the round-trip

  1. Send “ping” from your iPhone to the Mac’s iMessage.
  2. OpenClaw log should print the inbound event:
    [personal-imessage] message from +15551234567: ping
  3. In the gateway UI, reply “pong”.
  4. Your phone should receive “pong” instantly, no green bubbles.

Attachments? Send a photo and watch BlueBubbles stream the file via /api/v1/attachments/:guid. OpenClaw exposes it to the agent as message.attachments[0].downloadUrl.

Common pitfalls and fixes

  • Mac goes to sleep: iMessage disconnects. Set pmset:
    $ sudo pmset -a displaysleep 0 sleep 0
  • PrivacyD blocks AppleScript: After macOS updates, the TCC database forgets BlueBubbles. Re-open System Settings and toggle Accessibility off/on.
  • Emoji reactions missing: Apple rolled out “Tapbacks v2” in Sonoma 14.4. BlueBubbles 1.9.1 will support them; watch PR #1443.
  • Blank sender names: Happens on secondary iCloud alias accounts. Enable “Contacts → iCloud → iCloud Contacts” or BlueBubbles can’t resolve names.

Security considerations

BlueBubbles tls-terminates with a self-signed cert by default. If you’re tunneling over the open Internet, at minimum:

  • Generate a LetsEncrypt cert and configure Server → SSL in the BlueBubbles settings.
  • Leave allowedOrigins empty so any Origin header is rejected. Add ClawCloud’s domain explicitly, e.g., https://agents.claw.cloud.
  • Use firewall-level IP allow lists. Tailscale or WireGuard nails this painlessly.
  • Rotate the password. The API exposes entire chat histories; treat it like prod database credentials.

When the legacy bridge is still the right call

If you’re running a single-user hobby agent on the same Mac that already has iMessage, BlueBubbles is probably overkill. The legacy channel works by piping AppleScript output straight into OpenClaw with zero network hops. Fewer moving parts means:

  • No TLS cert management.
  • No port forwarding.
  • No second process to monitor.

The trade-offs:

  • Outbound only. Your agent can send messages but can’t subscribe to incoming ones. You must poll the Messages DB which introduces 30-60 sec latency.
  • Attachments and reactions silently drop.
  • Every macOS point release breaks at least one UI label and you end up playing “find the button”.

I keep the legacy bridge installed but disabled. If BlueBubbles crashes during a demo, I flip a feature flag so at least outbound alerts still fire.

Troubleshooting checklist

If nothing works, run through this table-flip list before rage-filing a GitHub issue:

  • BlueBubbles logs: ~/Library/Logs/BlueBubbles. Look for “AppleScript Error” lines.
  • OpenClaw logs: set DEBUG=openclaw:*,imessage-* and watch the raw payloads.
  • WebSocket alive? wscat -c wss://your.bubble/ws and check you get a {"type":"ack"} within 5 sec.
  • Apple ID lockouts: Too many login attempts? Visit iforgot.apple.com.

Automating on top of the iMessage channel

Once the channel runs, your agent code is trivial. Example: Route photos from Mom to a private Slack DM using Composio’s Slack toolkit v0.4:

import { onMessage } from 'openclaw' import { slack } from '@composio/slack' onMessage('personal-imessage', async (msg) => { if (msg.attachments.length) { const fileUrl = msg.attachments[0].downloadUrl await slack.files.upload({ channels: 'U02SLACKME', initial_comment: `Photo from Mom ${msg.sender}`, file: await fetch(fileUrl).then(r => r.arrayBuffer()) }) } })

Zero extra work. The heavy lifting happens in the BlueBubbles layer.

Upgrading BlueBubbles without downtime

  1. Toggle “Maintenance Mode” in OpenClaw so new inbound messages queue.
  2. Run brew services stop bluebubbles or quit the app.
  3. Install the new DMG, replace the binary, reopen.
  4. Check /api/v1/ping again, then disable maintenance mode.

Total outage: ~30 sec. The WebSocket reconnect logic in @openclaw/channel-imessage-bluebubbles 0.6.3 backfills queued events so you don’t lose messages.

Future roadmap

The OpenClaw community is pushing on two items:

  • Native Apple Business Messages integration: Would bypass the Mac requirement entirely. Apple still restricts access to Business Chat partners, so don’t hold your breath.
  • Bridgeless on-device RCS/iMessage (iOS 17) via sideloaded helper: Experimental only, requires jailbroken device. Not something I’d ship to prod.

Until then, BlueBubbles is the most stable path.

Next step: put it in production

You now have OpenClaw talking to iMessage in real time. The last mile is making sure the Mac stays alive and backed up. Toss the machine on a UPS, wire it to your monitoring stack (check_http on port 12345), and set a calendar reminder to renew your TLS cert every 90 days. Once that’s in place, hook the iMessage channel into the rest of your agent workflow—scheduled tasks, browser actions, or 800+ Composio tools. Happy shipping.