Use this file to discover all available pages before exploring further.
@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.
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…).
Messages carries a wide range of formats through without transcoding. The SDK’s isImage / isVideo / isAudio helpers classify by the stored MIME type — there’s no allowlist.
import { IMessageSDK, MessageScheduler } from "@photon-ai/imessage-kit";const sdk = new IMessageSDK();const scheduler = new MessageScheduler( sdk, { debug: true }, { onSent: (task: any, result: any) => console.log(`Sent: ${task.id}`), onError: (task: any, error: Error) => console.error(`Failed: ${error.message}`), onComplete: (task: any) => console.log(`Completed: ${task.id}`), },);const id = scheduler.schedule({ to: "+1234567890", content: "Reminder!", sendAt: new Date(Date.now() + 5 * 60_000),});scheduler.scheduleRecurring({ to: "+1234567890", content: "Good morning!", startAt: new Date("2026-01-01T08:00:00"), interval: "daily", endAt: new Date("2026-12-31"),});scheduler.reschedule(id, new Date(Date.now() + 60_000));scheduler.cancel(id);scheduler.getPending();scheduler.destroy();
The constructor takes (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.
See for the full hook surface. Hooks take positional args — onBeforeSend(to, content), onAfterSend(to, result) — not a single object. definePlugin() is a helper that just returns its argument for type inference.
Every thrown error is an . The factory functions (PlatformError, DatabaseError, SendError, WebhookError, ConfigError) produce IMessageError instances tagged with the corresponding code — switch on error.code rather than instanceof for each subtype.