Understand content types with XMTP
When you build an agent with XMTP, all messages are encoded with a content type to ensure that an XMTP client knows how to encode and decode messages, ensuring interoperability and consistent display of messages across apps.
In addition, message payloads are transported as a set of bytes. This means that payloads can carry any content type that a client supports, such as plain text, JSON, or even non-text binary or media content.
At a high level, there are three categories of content types with XMTP:
- Standard
- Standards-track
- Custom
Standard content types
A standard content type is one that has undergone the XMTP Request for Comment (XRC) process and has been adopted as an XMTP Improvement Proposal (XIP).
Once adopted, a standard content type is bundled in XMTP client SDKs. All standard and standards-track content types are built into the Agent SDK—no installation or codec registration is required.
Text content type
An agent built with XMTP uses the text content type by default. This means that if your agent is sending plain text messages only, you don't need to perform any additional steps related to content types.
await ctx.sendText('gm');Receiving content types
When your agent receives messages with different content types, use event handlers to handle them appropriately:
// Listen for specific content types
agent.on('attachment', async (ctx) => {
// Handle attachment logic
});
agent.on('reaction', async (ctx) => {
// Handle reaction logic
});
agent.on('reply', async (ctx) => {
// Handle reply logic - replies now include the original message
const reply = ctx.message.content;
if (reply.inReplyTo) {
console.log(`Replying to: ${reply.inReplyTo.content}`);
}
});
// Use type guards for type-safe content access
agent.on('message', async (ctx) => {
if (ctx.isText()) {
// ctx.message.content is typed as string
} else if (ctx.isReply()) {
// ctx.message.content is typed as EnrichedReply
}
// For custom content types, access the content type directly
const contentType = ctx.message.contentType;
if (contentType?.typeId === 'my-custom-type') {
// Handle custom content logic
}
});Enriched messages
Messages now include additional context to make it easier to build rich messaging experiences:
- Reply count: Each message includes
numRepliesshowing how many replies it has received - Reactions array: Messages include a
reactionsarray containing all reaction messages - Parent message in replies: Reply messages include the original message being replied to via
inReplyTo - Expiration timestamp: For conversations with disappearing messages enabled, messages include an
expiresAtNstimestamp
const messages = await conversation.messages();
for (const message of messages) {
// Number of replies to a message
console.log(`Message ${message.id} has ${message.numReplies} replies`);
// Array of reaction messages
console.log(`Message ${message.id} has ${message.reactions.length} reactions`);
// Message expiration timestamp (for disappearing messages)
if (message.expiresAtNs !== undefined) {
const expiresAt = Number(message.expiresAtNs) / 1_000_000_000; // Convert nanoseconds to seconds
console.log(`Message expires at: ${new Date(expiresAt * 1000).toISOString()}`);
}
// Reply messages include the original message
if (ctx.isReply()) {
const reply = ctx.message.content;
console.log(`Replying to: ${reply.inReplyTo?.content}`);
}
}Standards-track content types
A standards-track content type is one that's being actively reviewed for adoption as a standard content type through the XIP process.
All standards-track content types are built into the Agent SDK and can be used with dedicated send methods:
- Group updates content type: Use to send group updates, such as name, description, and members.
- Remote attachment content type: Use
sendRemoteAttachment()to send attachments of any size. - Markdown content type: Use
sendMarkdown()to send rich formatted text messages with headers, lists, tables, and code blocks. - Reaction content type: Use
sendReaction()to send a quick and often emoji-based way to respond to a message. - Reply content type: Use
sendReply()to send a direct response to a specific message in a conversation. Users can select and reply to a particular message instead of sending a new one. - Onchain transaction reference content type: Use
sendTransactionReference()to send references to onchain transactions, such as crypto payments. - Onchain transaction content type: Use
sendWalletSendCalls()to support sending transactions to a wallet for execution.
Custom content types
Custom content types allow you to define your own schemas for messages that go beyond what is covered by standard or standards-track types. These are useful for experiments, domain-specific features, or app-specific behaviors.

