Reading attachments reports metadata and paths without modifying the originals. Optional conversion creates cached variants for CAF audio and GIF images. Sending stages a private copy for Messages to read, as described below.
#Reading attachments
imsg history --chat-id 42 --attachments --json
imsg watch --chat-id 42 --attachments --json
Every message JSON object contains an attachments array. CLI history and watch JSON preserve their historical behavior of populating attachment metadata even without --attachments; that flag controls human-readable display. RPC read methods populate the array only when their documented attachments flag is true. Otherwise it is empty. Per-attachment fields:
| Field | Type | Notes |
|---|---|---|
filename | string | Stored filename inside Messages' attachments dir. |
transfer_name | string | Original filename as sent. |
uti | string | Apple Uniform Type Identifier. |
mime_type | string | Best-effort MIME from UTI. |
total_bytes | int | Size in bytes. |
is_sticker | bool | True for sticker-pack attachments. |
missing | bool | True when the file couldn't be located on disk. |
original_path | string | Resolved absolute path under ~/Library/Messages/Attachments/. |
converted_path | string | Set only with --convert-attachments; see below. |
converted_mime_type | string | Set only with --convert-attachments. |
When an attachment is referenced in chat.db but the underlying file has been pruned (Messages can age out big files), missing is true and original_path may be empty.
#Converted variants
imsg history --chat-id 42 --attachments --convert-attachments --json
imsg watch --chat-id 42 --attachments --convert-attachments --json
This adds converted_path and converted_mime_type to attachments where conversion is supported:
- CAF audio → M4A. Messages' on-device voice memos are stored as CAF; most LLMs and downstream tools want M4A.
- GIF image → first-frame PNG. Useful when a static thumbnail is enough for downstream models.
Originals are never modified. Converted files live alongside in a cache directory and are reused on subsequent reads.
--convert-attachments requires ffmpeg on PATH. If ffmpeg is missing, the command still succeeds — converted_path is simply omitted from the output and the original metadata is unchanged.
brew install ffmpeg to enable.
#Sending attachments
imsg send --to "+14155551212" --file ~/Desktop/photo.jpg
imsg send --to "Jane Appleseed" --file ~/Desktop/voice.m4a
imsg send --chat-id 42 --file ~/Desktop/note.pdf
--file accepts any regular file. Audio files (.m4a, .caf, .aiff, …) ride the same code path as images and documents.
Before invoking AppleScript, imsg stages the file under ~/Library/Messages/Attachments/imsg/. Messages reads attachments from inside its own attachments directory more reliably than from ~/Desktop or ~/Downloads, particularly under newer macOS sandboxing.
The staged copies live under imsg/, distinct from Messages' own subdirectories, and are not pruned automatically. Clear them periodically if disk space matters.
For bridge-backed threaded replies, use send-rich --file or send-attachment --reply-to:
imsg send-rich --chat 'iMessage;-;+15551234567' \
--reply-to <messageGuid> --text "here it is" --file ~/Desktop/photo.jpg
imsg send-attachment --chat 'iMessage;-;+15551234567' \
--reply-to <messageGuid> --file ~/Desktop/photo.jpg
#Native voice messages
imsg send-attachment --chat 'iMessage;-;+15551234567' \
--file ~/Desktop/speech.mp3 --audio
--audio sends a native voice message through the bridge. Before dispatch, imsg converts a securely staged copy to mono 24 kHz Opus in a CAF container, using macOS's built-in /usr/bin/afconvert. No ffmpeg installation is needed. This applies to macOS-supported audio inputs, including MP3, M4A, WAV and CAF; the input extension alone does not determine its codec. Invalid audio or failed conversion stops the send, without an AppleScript fallback.
Setting the native audio-message flag on an ordinary MP3 is insufficient: Messages can show a 00:00 bubble that will not play inline even though the attachment contains valid audio. CAF/Opus preparation gives the native player the expected audio representation.
The original file is unchanged. The prepared CAF follows the same staged-copy lifetime described above, because Messages may read it after bridge acknowledgment. Without --audio, audio files remain ordinary attachments and are not transcoded. RPC send.attachment uses the same preparation when audio, is_audio or as_voice is true. Native voice messages require the bridge; --transport applescript does not support them.
#Why not just copy or upload?
The CLI's contract is "read what's there, send what you give it." Anything beyond that — bulk archival, cloud upload, format conversion at rest — is left to callers, who know their retention and privacy requirements. Conversion is limited to opt-in receive-side variants and the native voice-message representation requested by --audio; originals are never rewritten.
If you want a full archive workflow, pipe --attachments --json through your own scripts and copy the files out of ~/Library/Messages/Attachments/ yourself.