@photon-ai/advanced-imessage-kit
v1.16.0
Published
Powerful TypeScript iMessage SDK with real-time message processing
Downloads
4,790
Readme

Advanced iMessage Kit
A powerful TypeScript SDK for iMessage with real-time messaging support
Advanced iMessage Kit is a full-featured iMessage SDK for reading, sending, and automating iMessage conversations on macOS. Perfect for building AI agents, automation tools, and chat applications.
Enterprise
Advanced iMessage Kit is the enterprise edition provided by Photon.
- Provisioned iMessage Numbers – Dedicated numbers ready for production use
- Managed Hosting – Fully managed infrastructure for reliability and scale
- DevOps & Deployment Support – Production setup, monitoring, and scaling
- Enterprise Support – Direct support and custom integrations
📩 Enterprise inquiries:
Contact [email protected]
🌐 Book a Demo:
https://photon.codes
If you're looking for the free/open-source version, please use iMessage Kit instead.
Features
| Feature | Description | Method | Example |
| ---------------------------------------------------------- | --------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------- |
| Send Messages | Send text messages to any contact | messages.sendMessage() | message-send.ts |
| Reply to Messages | Reply inline to a specific message | messages.sendMessage() | message-reply.ts |
| Message Effects | Send with effects (confetti, fireworks, etc.) | messages.sendMessage() | message-effects.ts |
| Text Styles | Bold, italic, underline, strikethrough | messages.sendMessage() | message-styled.ts |
| Text Animations | Per-character animations (shake, ripple, etc.)| messages.sendMessage() | message-styled.ts |
| Send Rich Links | Send URLs with rich link previews | messages.sendMessage() | message-rich-link.ts |
| Schedule Messages | Send once or on a recurring schedule | scheduledMessages.createScheduledMessage() | scheduled-message-once.ts |
| Unsend Messages | Retract a sent message | messages.unsendMessage() | message-unsend.ts |
| Edit Messages | Edit a sent message | messages.editMessage() | message-edit.ts |
| Send Tapbacks | React with ❤️ 👍 👎 😂 ‼️ ❓ | messages.sendReaction() | message-reaction.ts |
| Query Messages | Search and filter message history | messages.getMessages() | message-search.ts |
| Destination Caller ID | See which of your addresses sent/received | messages.getMessages() | message-destination-caller-id.ts |
| Message History | View messages, reactions, polls, stickers | chats.getChatMessages() | message-history.ts |
| Send Attachments | Send images, files, documents | attachments.sendAttachment() | message-attachment.ts |
| Send Audio Messages | Send voice messages | attachments.sendAttachment() | message-audio.ts |
| Send Stickers | Send sticker as standalone message | attachments.sendSticker() | message-sticker.ts |
| Reply Stickers | Attach sticker to a message bubble | attachments.sendSticker() | message-reply-sticker.ts |
| Download Attachments | Download received files and media | attachments.downloadAttachment() | attachment-download.ts |
| Get Chats | List all conversations | chats.getChats() | chat-fetch.ts |
| Get Chat Participants | View group chat participants | chats.getChat() | chat-participants.ts |
| Manage Group Chats | Add/remove members, rename groups | chats.addParticipant() | chat-group.ts |
| Typing Indicators | Show "typing..." status | chats.startTyping() | message-typing.ts |
| Get Contacts | Fetch device contacts | contacts.getContacts() | contact-list.ts |
| Share Contact Card | Share your contact info in chat | contacts.shareContactCard() | message-contact-card.ts |
| Check iMessage Availability | Verify if contact uses iMessage | handles.getHandleAvailability() | service-check.ts |
| Server Info | Get server status and config | server.getServerInfo() | server-info.ts |
| Message Statistics | Get message counts and analytics | server.getMessageStats() | message-stats.ts |
| Create Polls | Create interactive polls in chat | polls.create() | poll-create.ts |
| Vote on Polls | Vote or unvote on poll options | polls.vote() | poll-vote.ts |
| Add Poll Options | Add options to existing polls | polls.addOption() | poll-add-option.ts |
| Find My Friends | Get friends' locations | icloud.refreshFindMyFriends() | findmy-friends.ts |
| Find My Watch | Watch friends' location changes in real-time | icloud.refreshFindMyFriends() | findmy-watch.ts |
| Set Chat Background | Set custom background image for chat | chats.setBackground() | background-set.ts |
| Remove Chat Background | Remove background from chat | chats.removeBackground() | background-remove.ts |
| Real-time Events | Listen for new messages, typing, etc. | sdk.on() | listen-simple.ts |
| Auto Reply | Build automated reply bots | sdk.on() | auto-reply-hey.ts |
Quick Start
Installation
npm install @photon-ai/advanced-imessage-kit
# or
bun add @photon-ai/advanced-imessage-kitBasic Usage
import { SDK } from "@photon-ai/advanced-imessage-kit";
const sdk = SDK({
serverUrl: "http://localhost:1234",
});
await sdk.connect();
sdk.on("new-message", (message) => {
console.log("New message:", message.text);
});
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Hello World!",
});
await sdk.close();Configuration
interface ClientConfig {
serverUrl?: string; // Server URL, defaults to "http://localhost:1234"
apiKey?: string; // API key (if server requires authentication)
logLevel?: "debug" | "info" | "warn" | "error"; // Log level, defaults to "info"
logToFile?: boolean; // Enable writing logs to ~/Library/Logs/AdvancedIMessageKit (default true)
}Remote Server Setup
If your SDK code runs on a different machine than the iMessage server:
serverUrlpoints to the server machinefilePathpoints to the SDK machine
The SDK reads local files first, then uploads the file bytes to the server.
Core Concepts
chatGuid Format
chatGuid is the unique identifier for a conversation. The format is service;-;address:
- iMessage DM:
iMessage;-;+1234567890oriMessage;-;[email protected] - SMS DM:
SMS;-;+1234567890 - Group chat:
iMessage;+;chat123456789 - Auto-detect:
any;-;+1234567890(SDK automatically detects the service type)
How to Get IDs
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Phone / Email │────▶│ Build chatGuid │────▶│ Send Message │
│ +1234567890 │ │ any;-;+123... │ │ sendMessage() │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ getChats() │────▶│ Get chat.guid │────▶│ Use for other │
│ List chats │ │ │ │ operations │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ sendMessage() │────▶│ Get message.guid│────▶│ edit/unsend │
│ Send message │ │ │ │ sendReaction │
└─────────────────┘ └─────────────────┘ └─────────────────┘Messages
Examples: message-send.ts | message-unsend.ts | message-edit.ts | message-reaction.ts | message-rich-link.ts | message-search.ts
Send Messages
// Send a text message
const message = await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Hello!",
});
// With subject and effect
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Happy Birthday!",
subject: "Wishes",
effectId: "com.apple.messages.effect.CKConfettiEffect",
});
// Reply to a message
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "This is a reply",
selectedMessageGuid: "original-message-guid",
});
// Send a rich link preview
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "https://photon.codes/",
richLink: true,
});Message Effects:
| Effect | effectId |
| ------------- | ------------------------------------------------- |
| Confetti | com.apple.messages.effect.CKConfettiEffect |
| Fireworks | com.apple.messages.effect.CKFireworksEffect |
| Balloons | com.apple.messages.effect.CKBalloonEffect |
| Hearts | com.apple.messages.effect.CKHeartEffect |
| Lasers | com.apple.messages.effect.CKHappyBirthdayEffect |
| Shooting Star | com.apple.messages.effect.CKShootingStarEffect |
| Sparkles | com.apple.messages.effect.CKSparklesEffect |
| Echo | com.apple.messages.effect.CKEchoEffect |
| Spotlight | com.apple.messages.effect.CKSpotlightEffect |
| Gentle | com.apple.MobileSMS.expressivesend.gentle |
| Loud | com.apple.MobileSMS.expressivesend.loud |
| Slam | com.apple.MobileSMS.expressivesend.impact |
| Invisible Ink | com.apple.MobileSMS.expressivesend.invisibleink |
Example: message-effects.ts
Text Styles & Animations
Apply per-range text formatting and whole-message character animations (requires Private API):
// Bold a portion of text
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "This is bold text",
textStyles: [{ start: 8, end: 12, bold: true }],
});
// Multiple styles in one message
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Bold here, italic there",
textStyles: [
{ start: 0, end: 9, bold: true },
{ start: 11, end: 23, italic: true },
],
});
// Character animation (applies to whole message)
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Ripple wave!",
textAnimation: "ripple",
});
// Combine all three layers
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Bold shaking fireworks!",
textStyles: [{ start: 0, end: 4, bold: true }],
textAnimation: "shake",
effectId: "com.apple.messages.effect.CKFireworksEffect",
});Text Style Properties (per range):
| Property | Type | Description |
| --------------- | ------- | -------------- |
| start | number | Start index |
| end | number | End index |
| bold | boolean | Bold |
| italic | boolean | Italic |
| underline | boolean | Underline |
| strikethrough | boolean | Strikethrough |
Text Animations (whole message):
| textAnimation | Description |
| --------------- | ----------- |
| "big" | Big |
| "small" | Small |
| "shake" | Shake |
| "nod" | Nod |
| "explode" | Explode |
| "ripple" | Ripple |
| "bloom" | Bloom |
| "jitter" | Jitter |
Example: message-styled.ts
Query Messages
// Get a single message
const message = await sdk.messages.getMessage("message-guid");
// Query messages
const messages = await sdk.messages.getMessages({
chatGuid: "iMessage;-;+1234567890",
limit: 50,
offset: 0,
sort: "DESC", // DESC = newest first, ASC = oldest first
before: Date.now(),
after: Date.now() - 86400000, // Last 24 hours
});
// Search messages
const results = await sdk.messages.searchMessages({
query: "keyword",
chatGuid: "iMessage;-;+1234567890", // Optional
limit: 20,
});
// Get counts
const total = await sdk.messages.getMessageCount();
const sent = await sdk.messages.getSentMessageCount();
const updated = await sdk.messages.getUpdatedMessageCount();Unsend Messages
await sdk.messages.unsendMessage({
messageGuid: "message-guid-to-unsend",
partIndex: 0, // Optional
});Example: message-unsend.ts
Edit Messages
const editedMessage = await sdk.messages.editMessage({
messageGuid: "message-guid-to-edit",
editedMessage: "New text content",
backwardsCompatibilityMessage: "New text content", // Optional, defaults to editedMessage
partIndex: 0, // Optional, defaults to 0
});
console.log(`Edited: ${editedMessage.guid}`);
console.log(`New text: ${editedMessage.text}`);
console.log(`Date edited: ${editedMessage.dateEdited}`);Example: message-edit.ts
Send Tapbacks
await sdk.messages.sendReaction({
chatGuid: "iMessage;-;+1234567890",
messageGuid: "target-message-guid",
reaction: "love", // love, like, dislike, laugh, emphasize, question
partIndex: 0, // Optional
});
// Remove a Tapback (prefix with -)
await sdk.messages.sendReaction({
chatGuid: "iMessage;-;+1234567890",
messageGuid: "target-message-guid",
reaction: "-love", // -love, -like, -dislike, -laugh, -emphasize, -question
});Example: message-reaction.ts
Other Message Operations
// Trigger message notification
await sdk.messages.notifyMessage("message-guid");
// Get embedded media
const media = await sdk.messages.getEmbeddedMedia("message-guid");Scheduled Messages
Examples: scheduled-message-once.ts | scheduled-message-recurring.ts | scheduled-message-manage.ts
Schedule a one-time message
const scheduled = await sdk.scheduledMessages.createScheduledMessage({
type: "send-message",
payload: {
chatGuid: "any;-;+1234567890",
message: "This is a scheduled message!",
method: "apple-script",
},
scheduledFor: Date.now() + 3 * 1000,
schedule: { type: "once" },
});Schedule a recurring message
const tomorrow9am = new Date();
tomorrow9am.setDate(tomorrow9am.getDate() + 1);
tomorrow9am.setHours(9, 0, 0, 0);
const daily = await sdk.scheduledMessages.createScheduledMessage({
type: "send-message",
payload: {
chatGuid: "any;-;+1234567890",
message: "Good morning!",
method: "apple-script",
},
scheduledFor: tomorrow9am.getTime(),
schedule: {
type: "recurring",
intervalType: "daily", // hourly, daily, weekly, monthly, yearly
interval: 1,
},
});Manage scheduled messages
const scheduledMessages = await sdk.scheduledMessages.getScheduledMessages();
const updated = await sdk.scheduledMessages.updateScheduledMessage(
"scheduled-id",
{
type: "send-message",
payload: {
chatGuid: "any;-;+1234567890",
message: "Updated message!",
method: "apple-script",
},
scheduledFor: Date.now() + 10 * 60 * 1000,
schedule: { type: "once" },
},
);
await sdk.scheduledMessages.deleteScheduledMessage("scheduled-id");Chats
Examples: chat-fetch.ts | chat-group.ts | message-typing.ts | background-set.ts | background-remove.ts
Get Chats
const chats = await sdk.chats.getChats({
withLastMessage: true, // Include last message
withArchived: false, // Include archived chats
offset: 0,
limit: 50,
});
// Get chat count
const count = await sdk.chats.getChatCount();Get Single Chat
const chat = await sdk.chats.getChat("chat-guid", {
with: ["participants", "lastMessage"],
});Get Chat Participants
Get participants from group chats and display them with contact names:
const chats = await sdk.chats.getChats();
const groups = chats.filter((chat) => chat.style === 43); // Filter group chats
// Get contacts for name mapping
const contacts = await sdk.contacts.getContacts();
const nameMap = new Map<string, string>();
for (const c of contacts) {
const name = c.displayName || c.firstName || "";
if (!name) continue;
for (const p of c.phoneNumbers || []) nameMap.set(p.address, name);
for (const e of c.emails || []) nameMap.set(e.address, name);
}
// Display participants
groups.forEach((group) => {
console.log(`Group: ${group.displayName || group.chatIdentifier}`);
group.participants?.forEach((p) => {
const name = nameMap.get(p.address);
const display = name ? `${name} <${p.address}>` : p.address;
console.log(` - ${display} (${p.service})`);
});
});Example: chat-participants.ts
Create Chat
const newChat = await sdk.chats.createChat({
addresses: ["+1234567890", "+0987654321"],
message: "Hello everyone!", // Optional initial message
service: "iMessage", // "iMessage" or "SMS"
method: "private-api", // "apple-script" or "private-api"
});Chat Status
// Mark as read/unread
await sdk.chats.markChatRead("chat-guid");
await sdk.chats.markChatUnread("chat-guid");
// Delete chat
await sdk.chats.deleteChat("chat-guid");Typing Indicators
// Show "typing..."
await sdk.chats.startTyping("chat-guid");
// Stop showing
await sdk.chats.stopTyping("chat-guid");Example: message-typing.ts
Get Chat Messages
const messages = await sdk.chats.getChatMessages("chat-guid", {
limit: 100,
offset: 0,
sort: "DESC",
before: Date.now(),
after: Date.now() - 86400000,
});Manage Group Chats
// Rename group
await sdk.chats.updateChat("chat-guid", {
displayName: "New Group Name",
});
// Add participant
await sdk.chats.addParticipant("chat-guid", "+1234567890");
// Remove participant
await sdk.chats.removeParticipant("chat-guid", "+1234567890");
// Leave group
await sdk.chats.leaveChat("chat-guid");Group Icon
// Set group icon
await sdk.chats.setGroupIcon("chat-guid", "/path/to/image.jpg");
// Get group icon
const iconBuffer = await sdk.chats.getGroupIcon("chat-guid");
// Remove group icon
await sdk.chats.removeGroupIcon("chat-guid");Example: chat-group.ts
Chat Background
Set, get, or remove custom background images for individual chats:
// Get current background info
const bgInfo = await sdk.chats.getBackground("chat-guid");
console.log(`Has background: ${bgInfo.hasBackground}`);
if (bgInfo.hasBackground) {
console.log(`Background ID: ${bgInfo.backgroundId}`);
console.log(`Image URL: ${bgInfo.imageUrl}`);
}
// Set a background image (using file path)
await sdk.chats.setBackground("chat-guid", {
filePath: "/path/to/image.png",
});
// Set a background image (using base64)
import fs from "node:fs";
const imageBuffer = fs.readFileSync("/path/to/image.png");
await sdk.chats.setBackground("chat-guid", {
fileData: imageBuffer.toString("base64"),
});
// Remove background
await sdk.chats.removeBackground("chat-guid");Examples: background-set.ts | background-remove.ts
Attachments
Examples: message-attachment.ts | message-audio.ts | message-reply-sticker.ts | attachment-download.ts
Send Attachments
const message = await sdk.attachments.sendAttachment({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/file.jpg",
fileName: "custom-name.jpg", // Optional
});Send Multiple Images In One Request
Use sendMultipartMessage() when you want to send multiple images together as one multipart iMessage request:
const message = await sdk.messages.sendMultipartMessage({
chatGuid: "iMessage;+;chat123456789",
parts: [
{
partIndex: 0,
filePath: "/path/to/image-1.jpg",
},
{
partIndex: 1,
filePath: "/path/to/image-2.jpg",
},
{
partIndex: 2,
filePath: "/path/to/image-3.jpg",
},
],
});Notes:
sendMultipartMessage()uploads each file first, then sends all parts together in a single/api/v1/message/multipartrequest.- This requires the Private API path on the server.
- The target
chatGuidmust already exist. If you only have a phone number/email, create or fetch the chat first. - If the SDK and server are on different machines, each
filePathmust exist on the SDK machine. The SDK uploads the file bytes to the server before sending.
Example: message-multipart-images.ts
Send Audio Messages
const message = await sdk.attachments.sendAttachment({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/audio.m4a",
isAudioMessage: true,
});Example: message-audio.ts
Send Stickers
Stickers can be sent in two ways:
Standalone Sticker - Sends as its own message (like sending an image, but with sticker styling):
await sdk.attachments.sendSticker({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/sticker.png",
});Example: message-sticker.ts
Reply Sticker (Tapback Sticker) - Attaches to an existing message bubble:
await sdk.attachments.sendSticker({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/sticker.png",
selectedMessageGuid: "target-message-guid", // Required for reply sticker
stickerX: 0.5, // Position X (0-1), default: 0.5
stickerY: 0.5, // Position Y (0-1), default: 0.5
stickerScale: 0.75, // Scale (0-1), default: 0.75
stickerRotation: 0, // Rotation in radians, default: 0
stickerWidth: 300, // Width in pixels, default: 300
});Example: message-reply-sticker.ts
Get Attachment Info
// Get attachment details
const attachment = await sdk.attachments.getAttachment("attachment-guid");
// Get total count
const count = await sdk.attachments.getAttachmentCount();Download Attachments
// Download attachment
const buffer = await sdk.attachments.downloadAttachment("attachment-guid", {
original: true, // Download original file
force: false, // Force re-download
width: 800, // Image width (for thumbnails)
height: 600, // Image height
quality: 80, // Image quality
});
// Download Live Photo video
const liveBuffer =
await sdk.attachments.downloadAttachmentLive("attachment-guid");
// Get blurhash (for placeholders)
const blurhash = await sdk.attachments.getAttachmentBlurhash("attachment-guid");Example: attachment-download.ts
Contacts
Example: contact-list.ts
Get Contacts
const contacts = await sdk.contacts.getContacts();Get Contact Card
// Get contact card by phone or email
const card = await sdk.contacts.getContactCard("+1234567890");
// {
// firstName: "John",
// lastName: "Doe",
// emails: ["[email protected]"],
// phones: ["+1234567890"],
// ...
// }Share Contact Card
Share your contact card with a chat:
// chatGuid is the chat identifier (e.g. the `guid` field you get from chat APIs/events)
// Check whether the SDK recommends sharing your contact card in this chat.
//
// Returns:
// - true: sharing is recommended (typically when the other side shared theirs and you haven't shared yours yet)
// - false: NOT recommended (e.g. you've already shared, OR the other side hasn't shared theirs yet)
const shouldShare = await sdk.contacts.shouldShareContact("chat-guid");
if (shouldShare) {
// Share your contact card (iMessage "Share Name and Photo")
await sdk.contacts.shareContactCard("chat-guid");
}Example: message-contact-card.ts
Handles
Examples: service-check.ts | handle-query.ts
A Handle represents a messaging address (phone number or email).
Query Handles
// Query handles
const result = await sdk.handles.queryHandles({
address: "+1234567890", // Optional, filter by address
with: ["chats"], // Optional, include related chats
offset: 0,
limit: 50,
});
// Get single handle
const handle = await sdk.handles.getHandle("handle-guid");
// Get total count
const count = await sdk.handles.getHandleCount();Check Service Availability
Check if a phone/email supports iMessage or FaceTime:
// First parameter is the address (phone or email), not handle guid
const hasIMessage = await sdk.handles.getHandleAvailability(
"+1234567890",
"imessage",
);
const hasFaceTime = await sdk.handles.getHandleAvailability(
"+1234567890",
"facetime",
);
// Choose service based on availability
const chatGuid = hasIMessage ? `iMessage;-;+1234567890` : `SMS;-;+1234567890`;Example: service-check.ts
Get Focus Status
const focusStatus = await sdk.handles.getHandleFocusStatus("handle-guid");Server
Examples: message-stats.ts | server-info.ts
Get Server Info
const info = await sdk.server.getServerInfo();
// {
// os_version: "14.0",
// server_version: "1.0.0",
// private_api: true,
// helper_connected: true,
// detected_icloud: "[email protected]",
// ...
// }Message Statistics
const stats = await sdk.server.getMessageStats();
// {
// total: 12345,
// sent: 5000,
// received: 7345,
// last24h: 50,
// last7d: 300,
// last30d: 1000,
// }Media Statistics
// All media stats
const mediaStats = await sdk.server.getMediaStatistics();
// Per-chat media stats
const chatMediaStats = await sdk.server.getMediaStatisticsByChat();Server Logs
const logs = await sdk.server.getServerLogs(100); // Get last 100 logsPolls
Examples: poll-create.ts | poll-vote.ts | poll-unvote.ts | poll-add-option.ts
Create Polls
const pollMessage = await sdk.polls.create({
chatGuid: "iMessage;-;+1234567890",
title: "What should we do?", // Optional
options: ["Option A", "Option B", "Option C"],
});
console.log("Poll GUID:", pollMessage.guid);Example: poll-create.ts
Add Poll Options
await sdk.polls.addOption({
chatGuid: "iMessage;-;+1234567890",
pollMessageGuid: "poll-message-guid",
optionText: "New Option D",
});Example: poll-add-option.ts
Vote on Polls
// Vote on a poll option
await sdk.polls.vote({
chatGuid: "iMessage;-;+1234567890",
pollMessageGuid: "poll-message-guid",
optionIdentifier: "option-uuid", // UUID of the option to vote for
});
// Remove your vote
await sdk.polls.unvote({
chatGuid: "iMessage;-;+1234567890",
pollMessageGuid: "poll-message-guid",
optionIdentifier: "option-uuid",
});Examples: poll-vote.ts | poll-unvote.ts
Parse Poll Messages
Use the poll-utils helper functions to parse and display poll messages:
import {
isPollMessage,
isPollVote,
parsePollDefinition,
parsePollVotes,
getPollSummary,
getOptionTextById,
} from "@photon-ai/advanced-imessage-kit";
sdk.on("new-message", (message) => {
if (isPollMessage(message)) {
if (isPollVote(message)) {
// Parse vote data
const voteData = parsePollVotes(message);
console.log("Votes:", voteData?.votes);
// Get option text for each vote
voteData?.votes.forEach((vote) => {
const optionText = getOptionTextById(vote.voteOptionIdentifier);
console.log(`${vote.participantHandle} voted for "${optionText}"`);
});
} else {
// Parse poll definition
const pollData = parsePollDefinition(message);
console.log("Poll title:", pollData?.title);
console.log("Options:", pollData?.options);
}
// Or get a formatted summary
console.log(getPollSummary(message));
}
});Note: Poll definitions are automatically cached when received. When a vote arrives, the SDK looks up the corresponding option text from the cache. If you receive a vote for a poll that was created before the SDK started, the option text won't be available and will show the UUID instead.
iCloud
Examples: findmy-friends.ts | findmy-watch.ts
Find My Friends
// Refresh and get friends' locations
const locations = await sdk.icloud.refreshFindMyFriends();
// Each location contains:
// - handle: phone number or email
// - coordinates: [latitude, longitude]
// - long_address: street address (optional)
// - expiry: timestamp when location expires (optional)
// Find specific friend
const friend = locations.find((loc) => loc.handle === "+1234567890");
if (friend) {
console.log(
`Coordinates: ${friend.coordinates[0]}, ${friend.coordinates[1]}`,
);
console.log(
`Maps: https://maps.google.com/?q=${friend.coordinates[0]},${friend.coordinates[1]}`,
);
if (friend.long_address) console.log(`Address: ${friend.long_address}`);
}
// List all friends
console.log(`All Friends (${locations.length}):`);
for (const loc of locations) {
console.log(`${loc.handle}: ${loc.coordinates[0]}, ${loc.coordinates[1]}`);
}Example: findmy-friends.ts
Find My Watch
Watch friends' location changes in real-time. Fetches initial locations on connect, then listens for live updates via the new-findmy-location event (server auto-refreshes every 30s):
import type { FindMyLocationItem } from "@photon-ai/advanced-imessage-kit";
// Fetch initial locations on ready
sdk.on("ready", async () => {
const locations = await sdk.icloud.refreshFindMyFriends();
for (const loc of locations) {
console.log(`${loc.handle}`);
console.log(` Coordinates: ${loc.coordinates[0]}, ${loc.coordinates[1]}`);
console.log(
` Maps: https://maps.google.com/?q=${loc.coordinates[0]},${loc.coordinates[1]}`,
);
if (loc.long_address) console.log(` Address: ${loc.long_address}`);
}
});
// Listen for real-time location updates
sdk.on("new-findmy-location", (location: FindMyLocationItem) => {
console.log(`${location.handle} updated:`);
console.log(
` Coordinates: ${location.coordinates[0]}, ${location.coordinates[1]}`,
);
});Example: findmy-watch.ts
Real-time Events
Examples: listen-simple.ts | listen-advanced.ts | auto-reply-hey.ts
The SDK receives real-time events from the server via Socket.IO.
Connection Events
sdk.on("ready", () => {
console.log("SDK connected and ready");
});
sdk.on("disconnect", () => {
console.log("Disconnected");
});
sdk.on("error", (error) => {
console.error("Error:", error);
});Message Events
// New message
sdk.on("new-message", (message) => {
console.log("New message:", message.text);
console.log("From:", message.handle?.address);
console.log("From me:", message.isFromMe);
});
// Message status update (delivered, read, etc.)
sdk.on("updated-message", (message) => {
if (message.dateRead) console.log("Message read");
else if (message.dateDelivered) console.log("Message delivered");
});
// Send failed
sdk.on("message-send-error", (data) => {
console.error("Send failed:", data);
});Chat Events
// Chat read status changed
sdk.on("chat-read-status-changed", ({ chatGuid, read }) => {
console.log(`Chat ${chatGuid} marked as ${read ? "read" : "unread"}`);
});Typing Indicators
sdk.on("typing-indicator", ({ display, guid }) => {
console.log(`${guid} ${display ? "is typing" : "stopped typing"}`);
});Group Events
sdk.on("group-name-change", (message) => {
console.log("Group renamed to:", message.groupTitle);
});
sdk.on("participant-added", (message) => {
console.log("Someone joined the group");
});
sdk.on("participant-removed", (message) => {
console.log("Someone was removed from the group");
});
sdk.on("participant-left", (message) => {
console.log("Someone left the group");
});
sdk.on("group-icon-changed", (message) => {
console.log("Group icon changed");
});
sdk.on("group-icon-removed", (message) => {
console.log("Group icon removed");
});Find My Friends Events
sdk.on("new-findmy-location", (location) => {
console.log(`${location.handle} location updated:`, location.coordinates);
});Remove Event Listeners
const handler = (message) => console.log(message);
sdk.on("new-message", handler);
// Remove specific listener
sdk.off("new-message", handler);
// Remove all listeners
sdk.removeAllListeners("new-message");Best Practices
Resource Management
// Graceful shutdown
process.on("SIGINT", async () => {
await sdk.close();
process.exit(0);
});Message Deduplication
The SDK includes built-in message deduplication to prevent processing duplicates during network instability:
// Clear processed messages (prevent memory leaks)
sdk.clearProcessedMessages(1000);
// Get processed message count
const count = sdk.getProcessedMessageCount();Error Handling
try {
await sdk.messages.sendMessage({
chatGuid: "invalid-guid",
message: "test",
});
} catch (error) {
if (error.response?.status === 404) {
console.error("Chat not found");
} else {
console.error("Send failed:", error.message);
}
}Auto-create Chats
When sending to a contact you've never messaged before, the SDK automatically creates the chat:
// Works even without existing chat history
await sdk.messages.sendMessage({
chatGuid: "any;-;+1234567890",
message: "Hi, this is John",
});
// SDK detects the chat doesn't exist, creates it, then sendsExamples
Run any example with Bun:
bun run examples/<filename>.tsGetting Started
| File | Description | | --------------------------------------------------------- | -------------------------------------- | | listen-simple.ts | Listen with formatted output | | listen-advanced.ts | Listen with full JSON and startup info | | message-send.ts | Send text messages | | message-attachment.ts | Send attachments | | message-audio.ts | Send audio messages |
Message Operations
| File | Description | | ------------------------------------------------------------------------------- | --------------------- | | message-reply.ts | Reply to messages | | message-unsend.ts | Unsend messages | | message-edit.ts | Edit messages | | message-reaction.ts | Send Tapbacks | | message-effects.ts | Message effects | | message-styled.ts | Text styles & animations | | message-search.ts | Search messages | | message-history.ts | Message history | | message-destination-caller-id.ts | Destination caller ID |
Chats & Groups
| File | Description | | ------------------------------------------------------- | ---------------------- | | chat-fetch.ts | Get chat list | | chat-participants.ts | Get group participants | | chat-group.ts | Manage groups | | message-typing.ts | Typing indicators | | background-set.ts | Set chat background | | background-remove.ts | Remove chat background |
Contacts & Services
| File | Description | | --------------------------------------------------------------- | ----------------------------- | | contact-list.ts | Get contacts | | message-contact-card.ts | Share contact card | | service-check.ts | Check iMessage availability | | message-service-check.ts | Monitor message service types |
Attachments & Media
| File | Description | | --------------------------------------------------------------- | ------------------------ | | attachment-download.ts | Download attachments | | message-sticker.ts | Send standalone stickers | | message-reply-sticker.ts | Send reply stickers |
Polls
| File | Description | | --------------------------------------------------- | ---------------- | | poll-create.ts | Create polls | | poll-vote.ts | Vote on polls | | poll-unvote.ts | Unvote on polls | | poll-add-option.ts | Add poll options |
Server & Advanced
| File | Description | | ------------------------------------------------- | -------------------- | | server-info.ts | Server info and logs | | message-stats.ts | Message statistics | | findmy-friends.ts | Find My Friends | | findmy-watch.ts | Find My Watch | | auto-reply-hey.ts | Auto reply bot |
LLMs
Download llms.txt for language model context:
License
MIT License
