Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. Recommended for iMessage integration due to its richer API and easier setup compared to the legacy imsg channel.Documentation Index
Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt
Use this file to discover all available pages before exploring further.
Current OpenClaw releases bundle BlueBubbles, so normal packaged builds do not need a separate
openclaw plugins install step.Overview
- Runs on macOS via the BlueBubbles helper app (bluebubbles.app).
- Recommended/tested: macOS Sequoia (15). macOS Tahoe (26) works; edit is currently broken on Tahoe, and group icon updates may report success but not sync.
- OpenClaw talks to it through its REST API (
GET /api/v1/ping,POST /message/text,POST /chat/:id/*). - Incoming messages arrive via webhooks; outgoing replies, typing indicators, read receipts, and tapbacks are REST calls.
- Attachments and stickers are ingested as inbound media (and surfaced to the agent when possible).
- Auto-TTS replies that synthesize MP3 or CAF audio are delivered as iMessage voice memo bubbles instead of plain file attachments.
- Pairing/allowlist works the same way as other channels (
/channels/pairingetc) withchannels.bluebubbles.allowFrom+ pairing codes. - Reactions are surfaced as system events just like Slack/Telegram so agents can “mention” them before replying.
- Advanced features: edit, unsend, reply threading, message effects, group management.
Quick start
Install BlueBubbles
Install the BlueBubbles server on your Mac (follow the instructions at bluebubbles.app/install).
Point webhooks at the gateway
Point BlueBubbles webhooks to your gateway (example:
https://your-gateway-host:3000/bluebubbles-webhook?password=<password>).Keeping Messages.app alive (VM / headless setups)
Some macOS VM / always-on setups can end up with Messages.app going “idle” (incoming events stop until the app is opened/foregrounded). A simple workaround is to poke Messages every 5 minutes using an AppleScript + LaunchAgent.Install a LaunchAgent
Save this as This runs every 300 seconds and on login. The first run may trigger macOS Automation prompts (
~/Library/LaunchAgents/com.user.poke-messages.plist:osascript → Messages). Approve them in the same user session that runs the LaunchAgent.Onboarding
BlueBubbles is available in interactive onboarding:BlueBubbles server address (e.g.,
http://192.168.1.100:1234).API password from BlueBubbles Server settings.
Webhook endpoint path.
pairing, allowlist, open, or disabled.Phone numbers, emails, or chat targets.
Access control (DMs + groups)
- DMs
- Groups
- Default:
channels.bluebubbles.dmPolicy = "pairing". - Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
- Approve via:
openclaw pairing list bluebubblesopenclaw pairing approve bluebubbles <CODE>
- Pairing is the default token exchange. Details: Pairing
Contact name enrichment (macOS, optional)
BlueBubbles group webhooks often only include raw participant addresses. If you wantGroupMembers context to show local contact names instead, you can opt in to local Contacts enrichment on macOS:
channels.bluebubbles.enrichGroupParticipantsFromContacts = trueenables the lookup. Default:false.- Lookups run only after group access, command authorization, and mention gating have allowed the message through.
- Only unnamed phone participants are enriched.
- Raw phone numbers remain as the fallback when no local match is found.
Mention gating (groups)
BlueBubbles supports mention gating for group chats, matching iMessage/WhatsApp behavior:- Uses
agents.list[].groupChat.mentionPatterns(ormessages.groupChat.mentionPatterns) to detect mentions. - When
requireMentionis enabled for a group, the agent only responds when mentioned. - Control commands from authorized senders bypass mention gating.
Command gating
- Control commands (e.g.,
/config,/model) require authorization. - Uses
allowFromandgroupAllowFromto determine command authorization. - Authorized senders can run control commands even without mentioning in groups.
Per-group system prompt
Each entry underchannels.bluebubbles.groups.* accepts an optional systemPrompt string. The value is injected into the agent’s system prompt on every turn that handles a message in that group, so you can set per-group persona or behavioral rules without editing agent prompts:
chatGuid / chatIdentifier / numeric chatId for the group, and a "*" wildcard entry provides a default for every group without an exact match (same pattern used by requireMention and per-group tool policies). Exact matches always win over the wildcard. DMs ignore this field; use agent-level or account-level prompt customization instead.
Worked example: threaded replies and tapback reactions (Private API)
With the BlueBubbles Private API enabled, inbound messages arrive with short message IDs (for example[[reply_to:5]]) and the agent can call action=reply to thread into a specific message or action=react to drop a tapback. A per-group systemPrompt is a reliable way to keep the agent choosing the right tool:
ACP conversation bindings
BlueBubbles chats can be turned into durable ACP workspaces without changing the transport layer. Fast operator flow:- Run
/acp spawn codex --bind hereinside the DM or allowed group chat. - Future messages in that same BlueBubbles conversation route to the spawned ACP session.
/newand/resetreset the same bound ACP session in place./acp closecloses the ACP session and removes the binding.
bindings[] entries with type: "acp" and match.channel: "bluebubbles".
match.peer.id can use any supported BlueBubbles target form:
- normalized DM handle such as
+15555550123oruser@example.com chat_id:<id>chat_guid:<guid>chat_identifier:<identifier>
chat_id:* or chat_identifier:*.
Example:
Typing + read receipts
- Typing indicators: Sent automatically before and during response generation.
- Read receipts: Controlled by
channels.bluebubbles.sendReadReceipts(default:true). - Typing indicators: OpenClaw sends typing start events; BlueBubbles clears typing automatically on send or timeout (manual stop via DELETE is unreliable).
Advanced actions
BlueBubbles supports advanced message actions when enabled in config:Available actions
Available actions
- react: Add/remove tapback reactions (
messageId,emoji,remove). iMessage’s native tapback set islove,like,dislike,laugh,emphasize, andquestion. When an agent picks an emoji outside that set (for example👀), the reaction tool falls back toloveso the tapback still renders instead of failing the whole request. Configured ack reactions still validate strictly and error on unknown values. - edit: Edit a sent message (
messageId,text). - unsend: Unsend a message (
messageId). - reply: Reply to a specific message (
messageId,text,to). - sendWithEffect: Send with iMessage effect (
text,to,effectId). - renameGroup: Rename a group chat (
chatGuid,displayName). - setGroupIcon: Set a group chat’s icon/photo (
chatGuid,media) — flaky on macOS 26 Tahoe (API may return success but the icon does not sync). - addParticipant: Add someone to a group (
chatGuid,address). - removeParticipant: Remove someone from a group (
chatGuid,address). - leaveGroup: Leave a group chat (
chatGuid). - upload-file: Send media/files (
to,buffer,filename,asVoice).- Voice memos: set
asVoice: truewith MP3 or CAF audio to send as an iMessage voice message. BlueBubbles converts MP3 → CAF when sending voice memos.
- Voice memos: set
- Legacy alias:
sendAttachmentstill works, butupload-fileis the canonical action name.
Message IDs (short vs full)
OpenClaw may surface short message IDs (e.g.,1, 2) to save tokens.
MessageSid/ReplyToIdcan be short IDs.MessageSidFull/ReplyToIdFullcontain the provider full IDs.- Short IDs are in-memory; they can expire on restart or cache eviction.
- Actions accept short or full
messageId, but short IDs will error if no longer available.
- Templates:
{{MessageSidFull}},{{ReplyToIdFull}} - Context:
MessageSidFull/ReplyToIdFullin inbound payloads
Coalescing split-send DMs (command + URL in one composition)
When a user types a command and a URL together in iMessage — e.g.Dump https://example.com/article — Apple splits the send into two separate webhook deliveries:
- A text message (
"Dump"). - A URL-preview balloon (
"https://...") with OG-preview images as attachments.
channels.bluebubbles.coalesceSameSenderDms opts a DM into merging consecutive same-sender webhooks into a single agent turn. Group chats continue to key per-message so multi-user turn structure is preserved.
- When to enable
- Enabling
- Trade-offs
Enable when:
- You ship skills that expect
command + payloadin one message (dump, paste, save, queue, etc.). - Your users paste URLs, images, or long content alongside commands.
- You can accept the added DM turn latency (see below).
- You need minimum command latency for single-word DM triggers.
- All your flows are one-shot commands without payload follow-ups.
Scenarios and what the agent sees
| User composes | Apple delivers | Flag off (default) | Flag on + 2500 ms window |
|---|---|---|---|
Dump https://example.com (one send) | 2 webhooks ~1 s apart | Two agent turns: “Dump” alone, then URL | One turn: merged text Dump https://example.com |
Save this 📎image.jpg caption (attachment + text) | 2 webhooks | Two turns | One turn: text + image |
/status (standalone command) | 1 webhook | Instant dispatch | Wait up to window, then dispatch |
| URL pasted alone | 1 webhook | Instant dispatch | Instant dispatch (only one entry in bucket) |
| Text + URL sent as two deliberate separate messages, minutes apart | 2 webhooks outside window | Two turns | Two turns (window expires between them) |
| Rapid flood (>10 small DMs inside window) | N webhooks | N turns | One turn, bounded output (first + latest, text/attachment caps applied) |
Split-send coalescing troubleshooting
If the flag is on and split-sends still arrive as two turns, check each layer:Config actually loaded
Config actually loaded
openclaw gateway restart — the flag is read at debouncer-registry creation.Debounce window wide enough for your setup
Debounce window wide enough for your setup
Look at the BlueBubbles server log under Measure the gap between the
~/Library/Logs/bluebubbles-server/main.log:"Dump"-style text dispatch and the "https://..."; Attachments: dispatch that follows. Raise messages.inbound.byChannel.bluebubbles to comfortably cover that gap.Session JSONL timestamps ≠ webhook arrival
Session JSONL timestamps ≠ webhook arrival
Session event timestamps (
~/.openclaw/agents/<id>/sessions/*.jsonl) reflect when the gateway hands a message to the agent, not when the webhook arrived. A queued-second message tagged [Queued messages while agent was busy] means the first turn was still running when the second webhook arrived — the coalesce bucket had already flushed. Tune the window against the BB server log, not the session log.Memory pressure slowing reply dispatch
Memory pressure slowing reply dispatch
On smaller machines (8 GB), agent turns can take long enough that the coalesce bucket flushes before the reply completes, and the URL lands as a queued second turn. Check
memory_pressure and ps -o rss -p $(pgrep openclaw-gateway); if the gateway is over ~500 MB RSS and the compressor is active, close other heavy processes or bump to a larger host.Reply-quote sends are a different path
Reply-quote sends are a different path
Block streaming
Control whether responses are sent as a single message or streamed in blocks:Media + limits
- Inbound attachments are downloaded and stored in the media cache.
- Media cap via
channels.bluebubbles.mediaMaxMbfor inbound and outbound media (default: 8 MB). - Outbound text is chunked to
channels.bluebubbles.textChunkLimit(default: 4000 chars).
Configuration reference
Full configuration: ConfigurationConnection and webhook
Connection and webhook
channels.bluebubbles.enabled: Enable/disable the channel.channels.bluebubbles.serverUrl: BlueBubbles REST API base URL.channels.bluebubbles.password: API password.channels.bluebubbles.webhookPath: Webhook endpoint path (default:/bluebubbles-webhook).
Access policy
Access policy
channels.bluebubbles.dmPolicy:pairing | allowlist | open | disabled(default:pairing).channels.bluebubbles.allowFrom: DM allowlist (handles, emails, E.164 numbers,chat_id:*,chat_guid:*).channels.bluebubbles.groupPolicy:open | allowlist | disabled(default:allowlist).channels.bluebubbles.groupAllowFrom: Group sender allowlist.channels.bluebubbles.enrichGroupParticipantsFromContacts: On macOS, optionally enrich unnamed group participants from local Contacts after gating passes. Default:false.channels.bluebubbles.groups: Per-group config (requireMention, etc.).
Delivery and chunking
Delivery and chunking
channels.bluebubbles.sendReadReceipts: Send read receipts (default:true).channels.bluebubbles.blockStreaming: Enable block streaming (default:false; required for streaming replies).channels.bluebubbles.textChunkLimit: Outbound chunk size in chars (default: 4000).channels.bluebubbles.sendTimeoutMs: Per-request timeout in ms for outbound text sends via/api/v1/message/text(default: 30000). Raise on macOS 26 setups where Private API iMessage sends can stall for 60+ seconds inside the iMessage framework; for example45000or60000. Probes, chat lookups, reactions, edits, and health checks currently keep the shorter 10s default; broadening coverage to reactions and edits is planned as a follow-up. Per-account override:channels.bluebubbles.accounts.<accountId>.sendTimeoutMs.channels.bluebubbles.chunkMode:length(default) splits only when exceedingtextChunkLimit;newlinesplits on blank lines (paragraph boundaries) before length chunking.
Media and history
Media and history
channels.bluebubbles.mediaMaxMb: Inbound/outbound media cap in MB (default: 8).channels.bluebubbles.mediaLocalRoots: Explicit allowlist of absolute local directories permitted for outbound local media paths. Local path sends are denied by default unless this is configured. Per-account override:channels.bluebubbles.accounts.<accountId>.mediaLocalRoots.channels.bluebubbles.coalesceSameSenderDms: Merge consecutive same-sender DM webhooks into one agent turn so Apple’s text+URL split-send arrives as a single message (default:false). See Coalescing split-send DMs for scenarios, window tuning, and trade-offs. Widens the default inbound debounce window from 500 ms to 2500 ms when enabled without an explicitmessages.inbound.byChannel.bluebubbles.channels.bluebubbles.historyLimit: Max group messages for context (0 disables).channels.bluebubbles.dmHistoryLimit: DM history limit.
Actions and accounts
Actions and accounts
channels.bluebubbles.actions: Enable/disable specific actions.channels.bluebubbles.accounts: Multi-account configuration.
agents.list[].groupChat.mentionPatterns(ormessages.groupChat.mentionPatterns).messages.responsePrefix.
Addressing / delivery targets
Preferchat_guid for stable routing:
chat_guid:iMessage;-;+15555550123(preferred for groups)chat_id:123chat_identifier:...- Direct handles:
+15555550123,user@example.com- If a direct handle does not have an existing DM chat, OpenClaw will create one via
POST /api/v1/chat/new. This requires the BlueBubbles Private API to be enabled.
- If a direct handle does not have an existing DM chat, OpenClaw will create one via
iMessage vs SMS routing
When the same handle has both an iMessage and an SMS chat on the Mac (for example a phone number that is iMessage-registered but has also received green-bubble fallbacks), OpenClaw prefers the iMessage chat and never silently downgrades to SMS. To force the SMS chat, use an explicitsms: target prefix (for example sms:+15555550123). Handles without a matching iMessage chat still send through whatever chat BlueBubbles reports.
Security
- Webhook requests are authenticated by comparing
guid/passwordquery params or headers againstchannels.bluebubbles.password. - Keep the API password and webhook endpoint secret (treat them like credentials).
- There is no localhost bypass for BlueBubbles webhook auth. If you proxy webhook traffic, keep the BlueBubbles password on the request end-to-end.
gateway.trustedProxiesdoes not replacechannels.bluebubbles.passwordhere. See Gateway security. - Enable HTTPS + firewall rules on the BlueBubbles server if exposing it outside your LAN.
Troubleshooting
- If typing/read events stop working, check the BlueBubbles webhook logs and verify the gateway path matches
channels.bluebubbles.webhookPath. - Pairing codes expire after one hour; use
openclaw pairing list bluebubblesandopenclaw pairing approve bluebubbles <code>. - Reactions require the BlueBubbles private API (
POST /api/v1/message/react); ensure the server version exposes it. - Edit/unsend require macOS 13+ and a compatible BlueBubbles server version. On macOS 26 (Tahoe), edit is currently broken due to private API changes.
- Group icon updates can be flaky on macOS 26 (Tahoe): the API may return success but the new icon does not sync.
- OpenClaw auto-hides known-broken actions based on the BlueBubbles server’s macOS version. If edit still appears on macOS 26 (Tahoe), disable it manually with
channels.bluebubbles.actions.edit=false. coalesceSameSenderDmsenabled but split-sends (e.g.Dump+ URL) still arrive as two turns: see the split-send coalescing troubleshooting checklist — common causes are too-tight debounce window, session-log timestamps misread as webhook arrival, or a reply-quote send (which usesreplyToBody, not a second webhook).- For status/health info:
openclaw status --alloropenclaw status --deep.
Related
- Channel Routing — session routing for messages
- Channels Overview — all supported channels
- Groups — group chat behavior and mention gating
- Pairing — DM authentication and pairing flow
- Security — access model and hardening