@photon-ai/imessage-kit is an MIT-licensed, macOS-only SDK that talks directly to the local Messages database and AppleScript bridge. Use it when you want to read, send, and automate iMessage on a Mac you control — automation tools, AI agents, chat-first apps.
This SDK is for talking to iMessage locally on a Mac you control. If you
want remote/hosted iMessage delivery, threaded replies, tapbacks, edits /
unsends, and live typing indicators, use
Spectrum for a unified API across platforms,
or
@photon-ai/advanced-imessage
when you need low-level iMessage control.Requirements
- OS: macOS only (reads the Messages SQLite database directly).
- Runtime: Node.js ≥ 20 or Bun ≥ 1.0.
- Permission: the process must have Full Disk Access granted. Open System Settings → Privacy & Security → Full Disk Access and add your terminal or IDE (Terminal, iTerm2, Warp, VS Code, Cursor…).
Install
better-sqlite3 as an optional peer dependency.
Quick start
IMessageConfig
SDK main configuration interface
IMessageConfig
SDK main configuration interface
| Option | Type | Description |
|---|---|---|
databasePath | string | Database path Default: ~/Library/Messages/chat.db |
webhook | WebhookConfig | Webhook configuration (optional) |
watcher | WatcherConfig | Watcher configuration (optional) |
retry | RetryConfig | Retry configuration (optional) |
tempFile | TempFileConfig | Temporary file configuration (optional) |
scriptTimeout | number | AppleScript execution timeout In milliseconds (default: 30000) |
maxConcurrent | number | Maximum concurrent sends Default: 5, 0 means unlimited |
debug | boolean | Debug mode (default: false) |
plugins | readonly Plugin[] | Plugin list (optional) |
Sending
sdk.send(to, content) auto-detects whether to is a recipient (phone number / email) or a chatId (group or DM).
Text
Images and files
Groups
GroupchatIds are returned by listChats():
Batch
maxConcurrentSends from the SDK config.
Querying messages
MessageFilter
Message query filter
MessageFilter
Message query filter
| Option | Type | Description |
|---|---|---|
unreadOnly | boolean | Only query unread messages |
excludeOwnMessages | boolean | Exclude messages sent by current user (default: true) |
sender | string | Filter by sender |
chatId | string | Filter by chat ID |
service | ServiceType | Filter by service type |
hasAttachments | boolean | Only query messages with attachments |
excludeReactions | boolean | Exclude tapback reactions from results |
since | Date | Only query messages after this time |
search | string | Search message text content (case-insensitive) |
limit | number | Limit number of results |
Unread messages
getUnreadMessages() groups results by sender:
Listing chats
ListChatsOptions
Options for listing chats
ListChatsOptions
Options for listing chats
| Option | Type | Description |
|---|---|---|
limit | number | Maximum number of chats to return |
type | 'all' | 'group' | 'dm' | Filter by chat type |
hasUnread | boolean | Only return chats with unread messages |
sortBy | 'recent' | 'name' | Sort order |
search | string | Search by display name (case-insensitive) |
Real-time watching
Auto-reply with the message chain
sdk.message(msg) returns a — a fluent builder for filter-then-respond patterns:
Attachment helpers
Common media formats
Messages carries a wide range of formats through without transcoding. The SDK’sisImage / isVideo / isAudio helpers classify by the stored MIME type — there’s no allowlist.
| Category | Typical formats |
|---|---|
| Documents | PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT, RTF |
| Images | JPG, PNG, GIF, HEIC, WEBP, AVIF |
| Contacts | VCF (vCard) |
| Data | CSV, JSON, XML |
| Archives | ZIP, RAR, 7Z |
| Media | MP4, MOV, MP3, M4A |
Scheduling
MessageScheduler
(sdk, config?, events?) — three positional args. Config is ; one-shot task shape is and recurring is . The scheduler loop is internal — no start() call needed.
Reminders — natural-language scheduling
- Duration:
"5 minutes","2 hours","1 day","30 seconds","1 week" - Time:
"5pm","5:30pm","17:30" - Day + time:
"tomorrow 9am","friday 2pm"
Plugins
onBeforeSend(to, content), onAfterSend(to, result) — not a single object. definePlugin() is a helper that just returns its argument for type inference.
Error handling
PlatformError, DatabaseError, SendError, WebhookError, ConfigError) produce IMessageError instances tagged with the corresponding code — switch on error.code rather than instanceof for each subtype.
Links
- Source: github.com/photon-hq/imessage-kit
- LLM context: llms.txt, or
use context7: photon-hq/imessage-kit - Discord: Join