iMessage AI Bot with BlueBubbles Setup Guide
Build an AI assistant for iMessage using BlueBubbles on macOS. Full feature support including reactions and effects.
Molted Team
Molted.cloud
iMessage is the messaging platform Apple users actually use. Unlike WhatsApp or Telegram, there is no official bot API. But with BlueBubbles running on a Mac, you can build an AI assistant that responds to your iMessages. This guide shows how.
Why iMessage AI is different
Apple does not provide a public iMessage API. The only way to automate iMessage is:
- BlueBubbles - macOS app that exposes iMessage via REST API
- imsg - Native macOS integration (legacy approach)
- Jailbreak tweaks - iOS-based, not recommended
BlueBubbles is the most reliable option. It runs on a Mac (can be a Mac Mini) and provides a full API for sending/receiving messages.
Requirements
- A Mac that stays on - Mac Mini is perfect for this
- macOS with iMessage signed in
- BlueBubbles server app
- Full Disk Access permission for BlueBubbles
Install BlueBubbles server
- Download BlueBubbles from bluebubbles.app
- Install and open the app
- Grant Full Disk Access (System Settings → Privacy → Full Disk Access)
- Sign in to your iCloud account in Messages app
- Configure BlueBubbles server settings
Enable the API
In BlueBubbles settings:
- Enable "Private API" features for full functionality
- Set a server password
- Note your server URL (local or with Ngrok/Cloudflare tunnel)
Project setup
mkdir imessage-ai
cd imessage-ai
npm init -y
npm install axios @anthropic-ai/sdk dotenv expressCreate .env:
BLUEBUBBLES_URL=http://localhost:1234
BLUEBUBBLES_PASSWORD=your-server-password
ANTHROPIC_API_KEY=your-anthropic-keyBlueBubbles API client
require('dotenv').config();
const axios = require('axios');
const Anthropic = require('@anthropic-ai/sdk');
const express = require('express');
const BB_URL = process.env.BLUEBUBBLES_URL;
const BB_PASSWORD = process.env.BLUEBUBBLES_PASSWORD;
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const conversations = new Map();
// BlueBubbles API helper
const bb = axios.create({
baseURL: BB_URL,
params: { password: BB_PASSWORD },
});
// Send message via BlueBubbles
async function sendMessage(chatGuid, text) {
await bb.post('/api/v1/message/text', {
chatGuid,
message: text,
method: 'private-api', // Uses private API for delivery receipts
});
}
// Get recent messages from a chat
async function getChatMessages(chatGuid, limit = 10) {
const response = await bb.get(`/api/v1/chat/${chatGuid}/message`, {
params: { limit, sort: 'DESC' },
});
return response.data.data;
}
// Process incoming message with AI
async function handleMessage(chatGuid, text, senderName) {
// Get or create conversation
if (!conversations.has(chatGuid)) {
conversations.set(chatGuid, []);
}
const history = conversations.get(chatGuid);
// Add user message
history.push({ role: 'user', content: text });
// Trim history
if (history.length > 20) {
history.splice(0, history.length - 20);
}
try {
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
system: `You are a helpful iMessage assistant. Keep responses concise for mobile.
The user's name is ${senderName}.`,
messages: history,
});
const reply = response.content[0].text;
history.push({ role: 'assistant', content: reply });
await sendMessage(chatGuid, reply);
} catch (error) {
console.error('Error:', error);
await sendMessage(chatGuid, 'Sorry, I encountered an error.');
}
}
// Webhook server for BlueBubbles events
const app = express();
app.use(express.json());
app.post('/webhook', async (req, res) => {
const { type, data } = req.body;
if (type === 'new-message') {
const message = data;
// Ignore our own messages
if (message.isFromMe) {
return res.sendStatus(200);
}
const chatGuid = message.chats?.[0]?.guid;
const text = message.text;
const senderName = message.handle?.firstName || 'User';
if (chatGuid && text) {
await handleMessage(chatGuid, text, senderName);
}
}
res.sendStatus(200);
});
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
console.log('Configure BlueBubbles to send webhooks to http://localhost:3000/webhook');
});iMessage AI made easy
OpenClaw includes BlueBubbles integration. Configure once, works forever.
Start free trialConfigure BlueBubbles webhook
In BlueBubbles settings → Webhooks:
- Add new webhook URL:
http://localhost:3000/webhook - Enable "New Message" events
- Save
Now every incoming iMessage triggers your AI handler.
Handle images
app.post('/webhook', async (req, res) => {
const { type, data } = req.body;
if (type === 'new-message') {
const message = data;
if (message.isFromMe) return res.sendStatus(200);
const chatGuid = message.chats?.[0]?.guid;
// Check for attachments
if (message.attachments?.length > 0) {
const attachment = message.attachments[0];
if (attachment.mimeType?.startsWith('image/')) {
// Download attachment
const imageResponse = await bb.get(
`/api/v1/attachment/${attachment.guid}/download`,
{ responseType: 'arraybuffer' }
);
const base64 = Buffer.from(imageResponse.data).toString('base64');
const aiResponse = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{
role: 'user',
content: [
{
type: 'image',
source: {
type: 'base64',
media_type: attachment.mimeType,
data: base64,
},
},
{
type: 'text',
text: message.text || 'What is in this image?',
},
],
}],
});
await sendMessage(chatGuid, aiResponse.content[0].text);
return res.sendStatus(200);
}
}
// Handle text messages
if (message.text) {
await handleMessage(chatGuid, message.text, message.handle?.firstName);
}
}
res.sendStatus(200);
});Group chat support
app.post('/webhook', async (req, res) => {
const { type, data } = req.body;
if (type === 'new-message') {
const message = data;
const chat = message.chats?.[0];
const isGroupChat = chat?.participants?.length > 1;
// In group chats, only respond if mentioned
if (isGroupChat) {
const botName = 'AI'; // Your trigger word
if (!message.text?.toLowerCase().includes(botName.toLowerCase())) {
return res.sendStatus(200);
}
}
// ... rest of handler
}
});Reactions and effects
BlueBubbles Private API supports iMessage features:
// Send a reaction
await bb.post('/api/v1/message/react', {
chatGuid,
messageGuid: originalMessageGuid,
reaction: 'love', // love, like, dislike, laugh, emphasize, question
});
// Send with effect
await bb.post('/api/v1/message/text', {
chatGuid,
message: 'Congrats!',
method: 'private-api',
effectId: 'com.apple.messages.effect.CKConfettiEffect',
});Full iMessage AI experience
OpenClaw on Molted supports reactions, effects, and group chats. No code needed.
Keep it running
For 24/7 operation:
# Install PM2
npm install -g pm2
# Start with PM2
pm2 start index.js --name imessage-ai
# Auto-start on boot
pm2 startup
pm2 saveAlso configure your Mac:
- Disable sleep (System Settings → Energy Saver)
- Enable "Prevent automatic sleeping when display is off"
- Set BlueBubbles to launch at login
Limitations
- Requires a Mac - No way around this for iMessage
- Private API risks - Apple could break it with updates
- Single account - One iMessage account per Mac
- No business API - This is for personal use
OpenClaw alternative
OpenClaw supports BlueBubbles as a channel. If you have BlueBubbles running, you can:
- Deploy OpenClaw (Molted or self-hosted)
- Configure BlueBubbles URL and password
- Enable the iMessage channel
OpenClaw handles webhooks, conversation state, and multi-model support automatically.
Related guides
- ChatGPT on WhatsApp - Cross-platform alternative
- Mac Mini AI Server - Full setup guide for Mac-based AI
- Signal AI Bot - Privacy-focused alternative
iMessage AI assistant
OpenClaw with BlueBubbles. Full iMessage support on your Mac Mini.
24-hour free trial · No credit card required · Cancel anytime