> ## Documentation Index
> Fetch the complete documentation index at: https://docs.photon.codes/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Use Stable documentation by default. Honor an explicit Beta request or a URL under /docs/beta/. If the requested version conflicts with the installed CLI package or API origin, clarify the target before writing integration code.
> Pages under /docs/beta/ document Beta; other product pages document Stable. Keep the CLI package, commands, API origin, and credentials within the selected version. State the documentation version in your answer.
> For MCP search, always pass version: Stable or version: Beta. Unfiltered search mixes both versions. For filesystem reads, keep Beta queries under /beta/ and exclude /beta/ from Stable queries; discover paths before reading them.
> The public docs base is https://photon.codes/docs. Convert MCP page paths to public URLs under that base, preserving /beta/ when present. Read https://photon.codes/docs/skill.md for version selection and https://photon.codes/docs/llms.txt for the version indexes.

# Native iMessage message metadata

> Read curated Apple delivery, formatting, attachment, and reaction fields.

Cloud iMessage messages include a curated set of native metadata in addition
to Spectrum's cross-platform fields. The same fields are available on ordinary
inbound messages, messages returned by `space.getMessage()`, and native
outbound results. Synthetic events such as read receipts and group changes may
omit them.

When a streamed text send requires native edits, its returned record exposes
the final `nativeText` but omits the first-send metadata snapshot because those
lifecycle and formatting values no longer describe the edited message. Use
`space.getMessage()` when you need the current native state afterward.

| Use case | Fields |
| - | - |
| Delivery and lifecycle | `isSent`, `isDelivered`, `isDeliveredQuietly`, `didNotifyRecipient`, `isDelayed`, `sendErrorCode`, and the `dateDelivered`, `dateRead`, `datePlayed`, `dateEdited`, `dateRetracted`, and `dateExpressiveSendPlayed` timestamps |
| Native text | `nativeText`, `formatting`, `mentions`, `subject`, `balloonBundleId`, and `expressiveSendStyleId` |
| Attachments | `attachmentMetadata`, including transfer state, UTI, sticker/hidden status, original GUID, and Live Photo companion kind |
| Reactions and stickers | `appliedReactions`, `placedStickers`, and `reactionRecord` when the native row is itself a tapback |
| Classification | `itemType`, `groupTitle`, `partCount`, `isAutoReply`, `isCorrupt`, `isExpirable`, `isServiceMessage`, `isSpam`, and `isSystemMessage` |

Use `imessage.is()` when consuming the unified Spectrum message stream. It
narrows the message to the iMessage-specific type:

```ts theme={null}
import { imessage } from "spectrum-ts/providers/imessage";

const message = await space.getMessage(messageId);

if (message && imessage.is(message)) {
  if ((message.sendErrorCode ?? 0) !== 0) {
    console.error("Apple send error", message.sendErrorCode);
  }

  console.log({
    sent: message.isSent,
    delivered: message.isDelivered,
    deliveredAt: message.dateDelivered,
    readAt: message.dateRead,
    editedAt: message.dateEdited,
    retractedAt: message.dateRetracted,
  });
}
```

The same guard works inside `for await (const [, message] of
spectrum.messages)`.

`nativeText` preserves Apple's original text because formatting and mention
ranges use UTF-16 offsets into that string. Spectrum can split native
multipart content into grouped messages, so rebuilding these offsets from
`message.content` is not reliable:

```ts theme={null}
if (message && imessage.is(message)) {
  const text = message.nativeText;

  for (const mention of message.mentions ?? []) {
    console.log({
      address: mention.address,
      value: text?.slice(mention.start, mention.start + mention.length),
    });
  }
}
```

Attachment metadata describes Apple's transfer record; attachment bytes still
live in Spectrum attachment content:

```ts theme={null}
if (message && imessage.is(message)) {
  for (const attachment of message.attachmentMetadata ?? []) {
    if (attachment.transferState === "failed") {
      console.error(`Attachment ${attachment.guid} failed to transfer`);
    }
  }

  for (const applied of message.appliedReactions ?? []) {
    console.log(applied.sender?.address, applied.reaction.kind);
  }
}
```

`sendErrorCode === 0` means Apple recorded no send error. The metadata is
intentionally curated: Spectrum does not expose the raw Advanced iMessage
database row or unstable Apple-private fields.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.