bridge.md
August 10, 2026 · View on GitHub
This page is the command reference for imsg features that run through the injected IMCore bridge. Read Advanced IMCore first for the SIP, library-validation, entitlement, and privacy boundaries.
Most commands take --chat <guid>, where a direct chat looks like iMessage;-;+15551234567 and a group looks like iMessage;+;chat0000. Get the exact GUID from imsg chats --json, and run imsg status --json to inspect the selectors and RPC methods available on the current macOS version.
Messaging
Send an Apple URL preview. URL mode cannot be combined with text, effects, replies, or files.
imsg send-rich --chat 'iMessage;-;+15551234567' --url https://imsg.sh
Send rich text, a reply, or an attachment:
imsg send-rich --chat 'iMessage;-;+15551234567' --text "boom" \
--effect com.apple.MobileSMS.expressivesend.impact \
--reply-to <message-guid>
imsg send-rich --chat 'iMessage;-;+15551234567' \
--reply-to <message-guid> --text "here it is" --file ~/Pictures/image.jpg
imsg send-rich --chat 'iMessage;-;+15551234567' --text 'hello world' \
--format '[{"start":0,"length":5,"styles":["bold"]}]'
Formatting requires macOS 15 or newer. Multipart messages accept a JSON array of text parts:
imsg send-multipart --chat 'iMessage;+;chat0000' \
--parts '[{"text":"hi"},{"text":"there"}]'
Send regular or audio attachments:
imsg send-attachment --chat 'iMessage;-;+15551234567' \
--file ~/Pictures/image.jpg --transport auto
imsg send-attachment --chat 'iMessage;-;+15551234567' \
--reply-to <message-guid> --file ~/Pictures/image.jpg
imsg send-attachment --chat 'iMessage;-;+15551234567' \
--file ~/Desktop/audio.caf --audio
With --transport auto, a normal file can fall back to AppleScript only when
the bridge is unavailable or proves the request was not_started. It never
falls back after publication has an uncertain outcome, including timeout,
cancellation, a vanished or claimed request, or a malformed response.
--audio and --reply-to remain bridge-only.
Send a validated sticker on its own or attach it to an existing bubble part:
imsg send-sticker --chat 'iMessage;-;+15551234567' \
--file ~/Pictures/sticker.png
imsg send-sticker --chat 'iMessage;-;+15551234567' \
--file ~/Pictures/sticker.png --attach-to <message-guid> --target-part 0
Stickers are iMessage-only. They accept PNG/APNG, GIF, or JPEG images up to 500 KiB, 618×618 pixels, 100 frames, and 25 million decoded pixels. Standalone sends require selectors.stickerSend; attached stickers also require selectors.stickerAttach.
Bridge tapbacks support removal and custom emoji in addition to the standard reactions exposed by imsg react:
imsg tapback --chat 'iMessage;-;+15551234567' \
--message <message-guid> --kind love
imsg tapback --chat 'iMessage;-;+15551234567' \
--message <message-guid> --kind love --remove
Native polls
Create a poll with a visible caption:
imsg poll send --chat 'iMessage;-;+15551234567' \
--question 'Dinner?' --option 'Pizza' --option 'Sushi'
Messages does not render the payload title on the poll balloon, so poll send follows it with a best-effort caption. Use --comment to choose different visible text or --no-comment when the caller already sent the context.
Vote or remove a vote with one option selector:
imsg poll vote --chat-id 42 --poll <poll-guid> --option-index 2
imsg poll unvote --chat-id 42 --poll <poll-guid> --option-index 2
Poll creation requires selectors.pollPayloadMessage. Voting requires selectors.pollVoteMessage and the matching RPC capability reported by imsg status --json.
Message and chat mutation
Mutate an existing message:
imsg edit --chat 'iMessage;-;+15551234567' \
--message <message-guid> --new-text "actually..."
imsg unsend --chat 'iMessage;-;+15551234567' --message <message-guid>
imsg delete-message --chat 'iMessage;-;+15551234567' --message <message-guid>
imsg notify-anyways --chat 'iMessage;-;+15551234567' --message <message-guid>
Manage chats and participants:
imsg chat-create --addresses '+15551111111,+15552222222' --name 'Crew' --text 'gm'
imsg chat-name --chat 'iMessage;+;chat0000' --name 'Renamed'
imsg chat-photo --chat 'iMessage;+;chat0000' --file ~/Pictures/group.jpg
imsg chat-add-member --chat 'iMessage;+;chat0000' --address +15553333333
imsg chat-remove-member --chat 'iMessage;+;chat0000' --address +15553333333
imsg chat-leave --chat 'iMessage;+;chat0000'
imsg chat-delete --chat 'iMessage;+;chat0000'
imsg chat-mark --chat 'iMessage;+;chat0000' --read
chat-photo clears the photo when --file is omitted. chat-mark also accepts --unread. chat-create creates iMessage chats; SMS sending remains available through the standard imsg send --service sms path.
Account and identity
Inspect the active account, local history, and address capabilities:
imsg account
imsg account --local
imsg whois --address +15551234567 --type phone
imsg whois --address +15551234567 --local
imsg nickname --address +15551234567
imsg nickname --address +15551234567 --local
Inspect or explicitly share the local Messages Name & Photo:
imsg name-photo status --chat 'iMessage;-;+15551234567'
imsg name-photo share --chat 'iMessage;-;+15551234567'
status reports whether Messages would offer its native sharing action; it is not a durable record of prior sharing. share discloses the local profile to every chat participant and reports a request, not a delivery receipt. Call it only after explicit user confirmation of the destination.
Live bridge events
Merge bridge-pushed typing and alias events into the normal watch stream:
imsg watch --bb-events --json
This combines two independently ordered best-effort sources on serialized
stdout. Bridge events begin at the current event-log EOF and are non-resumable;
there is no ordering guarantee relative to database messages. RPC clients can
subscribe separately with bridge.events.subscribe and cancel its shared
subscription ID with watch.unsubscribe.
IPC layout
The bridge uses a UUID-keyed request queue so concurrent CLI invocations cannot overwrite one another:
~/Library/Containers/com.apple.MobileSMS/Data/
.imsg-bridge-ready PID lock set while injection is live
.imsg-rpc/in/<uuid>.json atomically published requests
.imsg-rpc/out/<uuid>.json per-request responses
.imsg-events.jsonl inbound asynchronous events
Set IMSG_BRIDGE_LEGACY_IPC=1 only when debugging against an older, unrebuilt helper that still uses the single-file IPC path.
Delivery outcomes and retry safety
The v2 client classifies failed mutations from queue ownership, using monotonic deadlines:
- An unpublished request, or an unclaimed request that the client atomically
owns and removes before a final empty response check, is
not_startedand retry-safe. - A
.processing.<pid>claim or an unreadable inbox isstill_in_flight. Files are preserved. - A published request that vanished without a response is
may_have_completed.
Malformed or unreadable responses after publication are also uncertain. The
legacy single-file command timeout is always still_in_flight because it has
no per-request claim proof. Never automatically retry a bridge mutation unless
the typed disposition is not_started; human-readable error wording is not a
retry contract.