Skip to main content

Overview

DecodedMessage is the enriched message type returned by list_enriched_messages() / listEnrichedMessages(). It includes decoded content, reactions, reply counts, and references to replied-to messages. Use DecodedMessage when building message UIs that need to display:
  • Reaction counts and emoji
  • Reply threads
  • Deleted message states
  • Full message metadata
For simple message lists without enrichment, use the lighter-weight Message type from list_messages() / listMessages().

Structure

Rust

Source: crates/xmtp_mls/src/messages/decoded_message.rs:108

Node.js

Source: bindings/node/src/messages/decoded_message.rs:11

Metadata

DecodedMessageMetadata (Rust)

Source: crates/xmtp_mls/src/messages/decoded_message.rs:84

Field descriptions

id

Unique message identifier as bytes. In Node.js bindings, exposed as hex string.

group_id / conversationId

Identifies which group/conversation this message belongs to. Bytes in Rust, hex string in Node.js.

sent_at_ns

Timestamp when the message was sent, in nanoseconds since Unix epoch.

kind

Message purpose:
  • Application - User-generated content (text, reactions, etc.)
  • MembershipChange - Group membership updates

sender_installation_id

Unique identifier for the sender’s installation. A single inbox can have multiple installations (devices).

sender_inbox_id

Inbox ID of the message sender. This is the primary identifier for users in XMTP.

delivery_status

Current delivery state:
  • Unpublished - Message prepared but not sent to network
  • Published - Successfully sent and confirmed
  • Failed - Send attempt failed

content_type

Identifies the content type:

inserted_at_ns

When the message was inserted into the local database (nanoseconds).

expires_at_ns

Optional expiration timestamp. After this time, the message should be deleted.

Content

MessageBody (Rust)

The content field is a MessageBody enum representing different content types:
Source: crates/xmtp_mls/src/messages/decoded_message.rs:61

DecodedMessageContent (Node.js)

In Node.js, content is wrapped in DecodedMessageContent with type-safe getters:
See Content Types for details on each type.

Reactions

The reactions field contains all reaction messages referencing this message.

Reaction filtering

Reactions include both added and removed:

Replies

num_replies

Count of messages that reference this message as a reply:

in_reply_to (Reply content only)

For messages with Reply content, the in_reply_to field contains the referenced message:
Rust structure:
Source: crates/xmtp_mls/src/messages/decoded_message.rs:32

Deleted messages

When a message is deleted, its content is replaced with:
The original content is not accessible, but metadata remains:
Deleted messages in reply chains:

Fallback text

Many content types include fallback text for clients that don’t support them:

Querying enriched messages

List with options

Performance considerations

Enriched messages are more expensive to load than basic messages because they:
  1. Decode message content
  2. Query related reactions
  3. Query reply counts
  4. Fetch referenced messages for replies
  5. Check deletion status
Best practices:
  • Use listMessages() for simple message lists
  • Use listEnrichedMessages() only when displaying reactions/replies
  • Implement pagination to limit batch size
  • Cache enriched data on the client side

Enrichment process

The enrichment process (from crates/xmtp_mls/src/messages/enrichment.rs):
  1. Decode content - Convert stored bytes to MessageBody
  2. Load reactions - Query reactions referencing each message
  3. Count replies - Count messages with reference_id matching message ID
  4. Load reply context - For reply messages, fetch the referenced message
  5. Apply deletions - Replace content with DeletedMessage if deleted
  6. Validate deletions - Ensure deletion is by sender or admin
Source: crates/xmtp_mls/src/messages/enrichment.rs:73

Example: Message thread UI

See also