Read

Groups

Messages encodes group chats with a different identifier shape than direct chats. imsg surfaces that distinction explicitly so callers don't have to parse handles themselves.

#What counts as a group

  • chat.chat_identifier or chat.guid contains ;+;, for example iMessage;+;chat1234567890.
  • SERVICE;-;TARGET is a direct 1:1 chat, for example iMessage;-;+15551234567. Deliberately not flagged as a group.
  • Direct chats typically use a single handle (phone or email) with no ;+;.

The is_group boolean on every chat object encodes this for you.

#Where the identifiers live

FieldSourceNotes
chat.ROWIDchat_idlocal rowidStable within one DB. Preferred routing handle.
chat.chat_identifierMessagesPortable group handle.
chat.guidMessagesPortable GUID. Often the same shape as chat_identifier for groups.
chat.display_nameMessagesOptional group name.
chat.account_id / account_login / last_addressed_handleMessagesRead-only routing diagnostics.
participantschat_handle_join + handleExternal handles only.

#Sending to a group

Pick the most stable identifier you have:

imsg send --chat-id 42 --text "hi"                                    # preferred (DB local)
imsg send --chat-identifier "iMessage;+;chat1234567890" --text "hi"   # portable
imsg send --chat-guid "iMessage;+;chat1234567890" --text "hi"         # portable

Group sends use AppleScript chat id "<handle>" (the "Jared pattern"). Attachments work the same as direct sends; see Send.

#Tahoe ghost-row failure

On macOS 26 (Tahoe), Messages.app sometimes reports AppleScript success while writing an empty unjoined SMS row instead of delivering to the target group. imsg send detects that ghost row by inspecting chat.db after the AppleScript call and reports an error rather than success.

This check is automatic for chat-target sends. Direct sends (--to) aren't affected.

#Inbound metadata (JSON)

imsg chats, imsg history, and imsg watch — and the JSON-RPC equivalents — all include the same group fields:

  • chat_id
  • chat_identifier
  • chat_guid
  • chat_name
  • account_id
  • account_login
  • last_addressed_handle
  • participants (array of handles)
  • is_group

Within one machine and one Messages database, chat_id is the preferred routing key. For sync across machines (or after a Messages reset), persist chat_identifier or chat_guid instead.

#Participants exclude the local user

participants is sourced from Messages' chat_handle_join table, which only stores external handles. Your own handle is implicit and message-specific.

When the distinction matters, combine these per-message fields:

  • is_from_me — outbound vs. inbound.
  • destination_caller_id (outbound only) — which of your numbers Messages routed through.

#Multiple local identities

Messages stores per-chat hints for which of your numbers should be used (account_id, account_login, last_addressed_handle). imsg exposes these as diagnostics, but its send cannot force a specific outbound number — AppleScript send has no from selector. To change the default for new outbound traffic, adjust Messages' Settings → iMessage section.

#Focused chat lookup

imsg group --chat-id 42
imsg group --chat-id 42 --json

imsg group prints id, identifier, GUID, name, service, is_group, participants, and routing hints for one chat. It works for direct chats too; treat it as a "chat detail" command rather than groups-only.

#Notes

  • Group send uses the chat handle, not buddy.
  • Outgoing messages from the local user can have an empty sender value. Prefer sender_name plus chat metadata when displaying who sent what.