Skip to content

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.

Node
await ctx.sendText('gm');

Receiving content types

When your agent receives messages with different content types, use event handlers to handle them appropriately:

Node
// 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 numReplies showing how many replies it has received
  • Reactions array: Messages include a reactions array 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 expiresAtNs timestamp
Node
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:

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.