Integrate

JSON output

Every read command supports --json. Output is newline-delimited JSON (NDJSON): one self-contained JSON object per line. This shape works equally well for streaming consumers and for batch readers that pipe through jq -s to materialize an array.

imsg chats --json | jq -s
imsg history --chat-id 42 --json | jq -s
imsg watch --chat-id 42 --json

Human progress, prompts, and warnings are written to stderr, not stdout. Stdout is reserved for parseable JSON so pipelines stay clean.

#Chat

Returned by imsg chats, imsg group, and embedded in nested chat references in messages.

FieldTypeNotes
idintchat.ROWID. Stable within one DB. Preferred routing handle.
namestringDisplay name, contact match, or raw handle fallback.
display_namestringGroup title from chat.display_name. Empty for direct chats without a custom name.
contact_namestringResolved Contacts name (when permission granted).
identifierstringchat.chat_identifier. Portable.
guidstringchat.guid. Portable.
servicestringiMessage, SMS, etc.
last_message_atISO8601Newest activity time.
is_groupboolTrue when identifier or guid contains ;+;.
participantsarray of stringsExternal handles only; local user implicit.
account_idstringRouting diagnostic. Read-only.
account_loginstringRouting diagnostic. Read-only.
last_addressed_handlestringRouting diagnostic. Read-only.

#Message

Returned by imsg history, imsg watch, and the JSON-RPC messages.history and watch.subscribe notifications.

FieldTypeNotes
idintrowid. Use as the --since-rowid cursor in watch.
chat_idintAlways present. Preferred routing handle.
chat_identifierstringPortable handle.
chat_guidstringPortable GUID.
chat_namestringDisplay name for the chat.
participantsarrayExternal handles.
is_groupboolTrue for group threads.
guidstringMessage GUID. Stable across machines.
reply_to_guidstringWhen set, this message is an inline reply to that GUID.
destination_caller_idstringOutgoing only — which of your numbers Messages routed through.
senderstringRaw handle. Empty for some self-sent messages.
sender_namestringResolved Contacts name when permission granted.
is_from_meboolTrue for outbound.
textstringPlain text. Recovered from attributedBody when text column is empty.
created_atISO8601Message timestamp.
attachmentsarrayPresent when --attachments is set. See below.
thread_originator_guidstringFor inline-reply threads.

#Reaction extensions

Present on imsg watch --reactions events:

FieldTypeNotes
is_reactionbooltrue for tapback events.
reaction_typestringlove, like, dislike, laugh, emphasis, question, or a custom emoji marker.
reaction_emojistringCustom emoji, when present.
is_reaction_addbooltrue for add, false for remove.
reacted_to_guidstringThe message guid this tapback targets.

history deliberately hides reaction rows so they don't duplicate the reacted message. Reaction events only surface in the live watch stream.

#Attachment

Inside the attachments array on a message:

FieldTypeNotes
filenamestringStored filename.
transfer_namestringOriginal filename as sent.
utistringApple UTI.
mime_typestringBest-effort MIME.
byte_sizeintSize in bytes.
is_stickerboolSticker-pack attachments.
missingboolUnderlying file not on disk.
pathstringResolved absolute path.
converted_pathstringPresent with --convert-attachments.
converted_mime_typestringPresent with --convert-attachments.

#Conventions

  • Every numeric field is a JSON number. id, chat_id, and byte_size are integers; nothing requires 64-bit JSON-string encoding.
  • Times are ISO 8601 with explicit timezone (typically Z).
  • Strings that aren't applicable are omitted, not set to null. Test with field in obj, not obj.field === null.
  • Booleans are explicit true / false, never 0/1.
  • Arrays are always present when documented (possibly empty).

#Stability

The JSON schema is treated as a public API. Field renames or removals are tracked in CHANGELOG.md with a "change" or "deprecation" note and gated to a minor release.

The 0.2.0 → 0.3.0 cycle did one large rename (camelCase → snake_case). Since 0.3.0 the schema has been additive only.