Skip to main content
The BlueBubbles Server can operate in two modes: a standard mode driven by AppleScript automation, and an enhanced mode that uses a private macOS framework to communicate directly with the Messages app at a lower level. This lower-level integration is called the Private API. It unlocks a set of features that AppleScript simply cannot reach — reactions, typing indicators, group management, and more — but it requires explicit opt-in on the server side before your SDK calls can use them.
The Private API features described on this page only work when Private API mode is enabled in your BlueBubbles Server settings. If the server does not have Private API enabled, calls to Private API-backed methods will fail or silently fall back to standard behavior. Enable it in the BlueBubbles Server app under Settings → Private API.

What the Private API unlocks

The following features are only available when the Private API is active on the server:
FeatureSDK method(s)
Create new chatsclient.chats.create()
Add participants to a groupclient.chats.addParticipantToChat()
Remove participants from a groupclient.chats.removeParticipantFromChat()
Send tapback reactionsclient.messages.sendReaction()
Start typing indicatorsclient.chats.startSendTypingIndicator()
Stop typing indicatorsclient.chats.stopSendTypingIndicator()
Mark chat as readclient.chats.markRead()
Mark chat as unreadclient.chats.markChatAsUnread() (requires macOS Ventura or later)
Rename a group chatclient.chats.updateAChat()
Set group chat iconclient.chats.setGroupIcon()
Remove group chat iconclient.chats.removeGroupIcon()
Leave a group chatclient.chats.leaveChat()
Delete a chatclient.chats.deleteChat()
Send with message effectsclient.messages.sendText() with effectId
Send with a subject lineclient.messages.sendText() with subject
Reply to a specific messageclient.messages.sendText() with selectedMessageGuid

Sending with method: "private-api"

When you call client.messages.sendText(), the server uses AppleScript by default. To explicitly route the send through the Private API — which enables richer features and better reliability — pass method: "private-api" in the request body.
await client.messages.sendText({
  requestBody: {
    chatGuid: "iMessage;+;+15550001234",
    message: "Hello with Private API!",
    tempGuid: crypto.randomUUID(),
    method: "private-api",
  },
});
Some parameters automatically force the Private API even without setting method explicitly. These are:
  • subject — attaches a subject line to the message
  • effectId — sends the message with an iMessage effect (e.g., balloons, confetti)
  • selectedMessageGuid — sends the message as a reply to a specific prior message
// Send with a subject — automatically uses Private API
await client.messages.sendText({
  requestBody: {
    chatGuid: "iMessage;-;tim@apple.com",
    message: "Check out this announcement.",
    subject: "Important Update",
    tempGuid: crypto.randomUUID(),
  },
});

// Send with an iMessage effect — automatically uses Private API
await client.messages.sendText({
  requestBody: {
    chatGuid: "iMessage;+;+15550001234",
    message: "Happy Birthday!",
    effectId: "com.apple.MobileSMS.expressivesend.balloons",
    tempGuid: crypto.randomUUID(),
  },
});

// Reply to a specific message — automatically uses Private API
await client.messages.sendText({
  requestBody: {
    chatGuid: "iMessage;+;+15550001234",
    message: "Agreed!",
    selectedMessageGuid: "p:0/ABCDEF01-2345-6789-ABCD-EF0123456789",
    partIndex: 0,
    tempGuid: crypto.randomUUID(),
  },
});

Sending reactions

Reactions (tapbacks) are exclusively a Private API feature. Provide the chatGuid, the GUID of the message you’re reacting to, and one of the supported reaction strings.
await client.messages.sendReaction({
  requestBody: {
    chatGuid: "iMessage;+;+15550001234",
    selectedMessageGuid: "p:0/ABCDEF01-2345-6789-ABCD-EF0123456789",
    reaction: "love",
    partIndex: 0,
  },
});
Valid reaction values:
To addTo remove
love-love
like-like
dislike-dislike
laugh-laugh
emphasize-emphasize
question-question
Prefix the reaction name with - to remove a previously sent tapback.

Managing group participants

Adding and removing participants from a group chat both require the Private API. Include the participant’s phone number or email address (with country code for phone numbers).
const chatGuid = "iMessage;+;chat012345678901234567";

// Add a participant
await client.chats.addParticipantToChat({
  chatGuid,
  requestBody: {
    address: "+15550005678",
  },
});

// Remove a participant
await client.chats.removeParticipantFromChat({
  chatGuid,
});
Include the country code when specifying phone number addresses. For US numbers, prefix with 1 — for example, +15550005678.

Typing indicators

You can show and hide the typing indicator in a conversation. The indicator automatically clears when a message is sent to that chat.
const chatGuid = "iMessage;+;+15550001234";

// Show the typing indicator
await client.chats.startSendTypingIndicator({ chatGuid, requestBody: null });

// ... compose the message ...

// Hide the typing indicator explicitly (also clears on send)
await client.chats.stopSendTypingIndicator({ chatGuid });

Creating a new chat

Creating a new iMessage conversation requires the Private API. Pass an array of participant addresses and an optional opening message.
const newChat = await client.chats.create({
  requestBody: {
    addresses: ["+15550001234", "+15550005678"],
    message: "Hey everyone, welcome to the group!",
  },
});

console.log(newChat.data.guid); // "iMessage;+;chat..."

Marking chats as read and unread

1

Mark as read

Calling markRead marks the chat as read on the macOS server and notifies other BlueBubbles clients to update their unread state.
await client.chats.markRead({
  chatGuid: "iMessage;+;+15550001234",
  requestBody: {},
});
2

Mark as unread

Calling markChatAsUnread marks the chat as unread. On macOS Ventura and later, this also updates the unread state for other Apple devices signed into the same account. On earlier macOS versions, the event is dispatched to BlueBubbles clients only.
await client.chats.markChatAsUnread({
  chatGuid: "iMessage;+;+15550001234",
  requestBody: {},
});
markChatAsUnread requires macOS Ventura or later for the change to propagate to iOS devices. On earlier macOS versions, only BlueBubbles clients will reflect the unread state.

Minimum server version requirements

Some Private API features require a specific minimum version of the BlueBubbles Server:
FeatureMinimum Server Version
Create chat0.3.0
Add / remove participant0.3.0
Rename group chat0.3.0
Mark chat as read1.1.0
Delete chat1.3.0
Mark chat as unread1.4.0
partIndex on reactions1.4.0
You can check the running server version at any time by calling await client.server.getServerMetadata() and inspecting the version field in the response.