Documentation Index Fetch the complete documentation index at: https://mintlify.com/xmtp/libxmtp/llms.txt
Use this file to discover all available pages before exploring further.
This guide covers sending messages, working with different content types, and managing message delivery in LibXMTP groups.
Sending Text Messages
Basic Message Sending
Send a simple text message:
use xmtp_mls :: groups :: send_message_opts :: SendMessageOpts ;
use xmtp_content_types :: text :: TextCodec ;
use xmtp_content_types :: ContentCodec ;
// Encode text content
let text_content = TextCodec :: encode ( "Hello, world!" . to_string ()) ? ;
let encoded_bytes = encoded_content_to_bytes ( text_content ) ? ;
// Send the message
let message_id = group . send_message ( & encoded_bytes , SendMessageOpts :: default ()) . await ? ;
Messages are sent as encoded content that specifies the content type. This allows receivers to properly decode and display the message.
Send Message Options
Control message behavior with SendMessageOpts:
use xmtp_mls :: groups :: send_message_opts :: SendMessageOptsBuilder ;
// Configure send options
let opts = SendMessageOptsBuilder :: default ()
. should_push ( true ) // Trigger push notifications
. build () ? ;
let message_id = group . send_message ( & encoded_bytes , opts ) . await ? ;
Optimistic Sending
Send messages without waiting for network confirmation:
// Message is stored locally and queued for sending
let message_id = group . send_message_optimistic (
& encoded_bytes ,
SendMessageOpts :: default ()
) ? ;
// Later, publish all pending messages
group . publish_messages () . await ? ;
Optimistic messages are visible locally immediately but may fail to send. Always call publish_messages() to ensure delivery.
Content Types
LibXMTP supports multiple content types for rich messaging.
Text Content
use xmtp_content_types :: text :: TextCodec ;
let content = TextCodec :: encode ( "Hello!" . to_string ()) ? ;
let bytes = encoded_content_to_bytes ( content ) ? ;
group . send_message ( & bytes , SendMessageOpts :: default ()) . await ? ;
Reactions
Send reactions to messages:
use xmtp_content_types :: reaction :: { ReactionCodec , ReactionAction };
use xmtp_proto :: xmtp :: mls :: message_contents :: content_types :: ReactionV2 ;
let reaction = ReactionV2 {
reference : hex :: encode ( & target_message_id ), // Message being reacted to
action : ReactionAction :: Added as i32 ,
content : "👍" . to_string (),
schema : 1 ,
};
let encoded = ReactionCodec :: encode ( reaction ) ? ;
let bytes = encoded_content_to_bytes ( encoded ) ? ;
group . send_message ( & bytes , SendMessageOpts :: default ()) . await ? ;
Replies
Reply to specific messages:
use xmtp_content_types :: reply :: { ReplyCodec , Reply };
let reply = Reply {
reference : hex :: encode ( & original_message_id ),
content_type : Some ( TextCodec :: content_type ()),
content : TextCodec :: encode ( "Thanks!" . to_string ()) ? ,
};
let encoded = ReplyCodec :: encode ( reply ) ? ;
let bytes = encoded_content_to_bytes ( encoded ) ? ;
group . send_message ( & bytes , SendMessageOpts :: default ()) . await ? ;
Delete Messages
Delete your own messages or (as super admin) others’ messages:
use xmtp_proto :: xmtp :: mls :: message_contents :: content_types :: DeleteMessage ;
use xmtp_content_types :: delete_message :: DeleteMessageCodec ;
let delete_msg = DeleteMessage {
message_id : hex :: encode ( & message_id_to_delete ),
};
let encoded = DeleteMessageCodec :: encode ( delete_msg ) ? ;
let bytes = encoded_content_to_bytes ( encoded ) ? ;
let deletion_id = group . send_message ( & bytes , SendMessageOpts :: default ()) . await ? ;
Or use the helper method:
// Delete a message (validates permissions automatically)
let deletion_id = group . delete_message ( & message_id ) . await ? ;
Reading Messages
Retrieve messages with filters:
use xmtp_db :: group_message :: MsgQueryArgs ;
let args = MsgQueryArgs :: default ()
. sent_after_ns ( Some ( start_time ))
. sent_before_ns ( Some ( end_time ))
. limit ( Some ( 50 ));
let messages = group . find_messages ( & args ) ? ;
for msg in messages {
println! ( "From: {}" , msg . sender_inbox_id);
println! ( "At: {}" , msg . sent_at_ns);
// Decode msg.decrypted_message_bytes based on content_type
}
Retrieve messages with reactions, replies, and deletion status:
let enriched_messages = group . find_enriched_messages ( & args ) ? ;
for msg in enriched_messages {
println! ( "Message: {}" , msg . id);
println! ( "Reactions: {}" , msg . reactions . len ());
if msg . is_deleted {
println! ( " [DELETED]" );
}
if let Some ( reply_id ) = msg . reply_to_message_id {
println! ( " Reply to: {}" , reply_id );
}
}
// Get stored message
let message = client . message ( message_id . clone ()) ? ;
// Get enriched message
let enriched = client . message_v2 ( message_id ) ? ;
Message Delivery Status
Check Delivery Status
use xmtp_db :: group_message :: DeliveryStatus ;
let messages = group . find_messages ( & MsgQueryArgs :: default ()) ? ;
for msg in messages {
match msg . delivery_status {
DeliveryStatus :: Published => {
println! ( "Message sent successfully" );
}
DeliveryStatus :: Unpublished => {
println! ( "Message pending (optimistic send)" );
}
DeliveryStatus :: Failed => {
println! ( "Message failed to send" );
}
}
}
Publish Pending Messages
Send all locally queued messages:
// Publish optimistic messages
group . publish_messages () . await ? ;
// Check if any messages failed
let failed_args = MsgQueryArgs :: default ()
. delivery_status ( Some ( vec! [ DeliveryStatus :: Failed ]));
let failed = group . find_messages ( & failed_args ) ? ;
if ! failed . is_empty () {
eprintln! ( "Warning: {} messages failed to send" , failed . len ());
}
Advanced Usage
Custom Content Types
Define your own content type:
use xmtp_proto :: xmtp :: mls :: message_contents :: EncodedContent ;
use prost :: Message ;
// Your custom message structure
#[derive( Message )]
struct CustomMessage {
#[prost(string, tag = "1" )]
pub custom_field : String ,
}
// Encode as EncodedContent
let custom = CustomMessage {
custom_field : "value" . to_string (),
};
let mut content_bytes = Vec :: new ();
custom . encode ( & mut content_bytes ) ? ;
let encoded = EncodedContent {
r#type : Some ( ContentTypeId {
authority_id : "your.authority" . to_string (),
type_id : "custom-type" . to_string (),
version_major : 1 ,
version_minor : 0 ,
}),
content : content_bytes ,
.. Default :: default ()
};
let mut bytes = Vec :: new ();
encoded . encode ( & mut bytes ) ? ;
group . send_message ( & bytes , SendMessageOpts :: default ()) . await ? ;
Message Count
Count messages matching criteria:
let count = group . count_messages ( & MsgQueryArgs :: default ()) ? ;
println! ( "Total messages: {}" , count );
// Count unread messages
let unread_args = MsgQueryArgs :: default ()
. sent_after_ns ( Some ( last_read_timestamp ));
let unread_count = group . count_messages ( & unread_args ) ? ;
Read Receipts
Get last read times by sender:
let read_times = group . get_last_read_times () ? ;
for ( sender_inbox_id , timestamp ) in read_times {
println! ( "{} last read at {}" , sender_inbox_id , timestamp );
}
TypeScript (Node.js) Examples
Send Text
List Messages
Add/Remove Members
import { Conversation } from '@xmtp/node-bindings'
// Send text message
const messageId = await conversation . sendText ( 'Hello, world!' )
// Send with optimistic delivery
const messageId = await conversation . sendText ( 'Hello!' , true )
// Publish pending messages
await conversation . publishMessages ()
Error Handling
use xmtp_mls :: groups :: GroupError ;
match group . send_message ( & bytes , opts ) . await {
Ok ( message_id ) => {
println! ( "Message sent: {}" , hex :: encode ( message_id ));
}
Err ( GroupError :: InvalidPermission ) => {
eprintln! ( "You don't have permission to send messages" );
}
Err ( GroupError :: NotFound ( NotFound :: MlsGroup )) => {
eprintln! ( "Group not found locally, try syncing" );
}
Err ( e ) => {
eprintln! ( "Failed to send: {}" , e );
}
}
Best Practices
Use Optimistic Sending Carefully
Optimistic sending improves UX but requires careful handling:
// Send optimistically
let msg_id = group . send_message_optimistic ( & bytes , opts ) ? ;
// Always publish later
match group . publish_messages () . await {
Ok ( _ ) => { /* success */ }
Err ( e ) => {
// Handle failure - maybe retry or notify user
eprintln! ( "Failed to publish: {}" , e );
}
}
Always sync to get latest messages:
group . sync () . await ? ;
let messages = group . find_messages ( & MsgQueryArgs :: default ()) ? ;
Handle Large Message Lists
Use pagination for performance:
let page_size = 50 ;
let mut offset = 0 ;
loop {
let args = MsgQueryArgs :: default ()
. limit ( Some ( page_size ));
let messages = group . find_messages ( & args ) ? ;
if messages . is_empty () {
break ;
}
// Process messages
process_messages ( messages );
offset += page_size ;
}
Decode Content Types Properly
Always check content type before decoding:
use xmtp_db :: group_message :: ContentType ;
for msg in messages {
match msg . content_type {
ContentType :: Text => {
let content = TextCodec :: decode ( msg . decrypted_message_bytes) ? ;
println! ( "Text: {}" , content );
}
ContentType :: Reaction => {
let reaction = ReactionV2 :: decode ( msg . decrypted_message_bytes . as_slice ()) ? ;
println! ( "Reaction: {}" , reaction . content);
}
_ => {
println! ( "Unknown content type" );
}
}
}
Next Steps
Permissions and Policies Control who can send messages and perform actions
Consent Management Manage user consent and block unwanted messages