List messages
Query messages from a conversation using filters including time ranges, delivery status, and content types. You can either retrieve the full message list or just get a message count for efficiency.
Method parameters
All parameters are optional and can be combined to create precise filters and sorts:
| Parameter | Type | Description |
|---|---|---|
limit | Int / Long / bigint | Maximum number of messages to return |
beforeNs / sentBeforeNs | Int64 / Long / bigint | Filter messages sent before this timestamp (nanoseconds) |
afterNs / sentAfterNs | Int64 / Long / bigint | Filter messages sent after this timestamp (nanoseconds) |
direction | SortDirection / string | Sort order: descending (default, newest first) or ascending (oldest first) |
deliveryStatus | MessageDeliveryStatus / string | Filter by delivery status: all (default), published, unpublished, or failed |
excludeContentTypes / excludedContentTypes | Array | Content types to exclude (e.g., reactions, read receipts). |
excludeSenderInboxIds | Array of strings | Inbox IDs whose messages should be excluded. |
insertedBeforeNs | Int64 / Long / bigint | Filter messages inserted into the local database before this timestamp (nanoseconds) |
insertedAfterNs | Int64 / Long / bigint | Filter messages inserted into the local database after this timestamp (nanoseconds) |
sortBy | SortBy / enum | Sort by SENT_TIME (default, when message was sent) or INSERTED_TIME (when message was added to local database) |
List messages with enriched metadata
The enrichedMessages() method retrieves messages from conversations with enriched metadata automatically included. This means reactions, replies, and other associated data are "baked in" to each message, eliminating the need for separate queries to fetch this information.
You may want to use enriched messages by default. The enrichedMessages() method provides better performance and simpler code.
Be sure to handle content types properly by using the generic content<T>() method with the appropriate type for reactions and replies.
List messages enriched with reaction metadata
Use reactions to get a list of reaction messages associated with the message.
// Retrieve messages with enriched data from a group
val groupMessages = group.enrichedMessages()
// Retrieve messages with enriched data from a DM
val dmMessages = dm.enrichedMessages()
// Access reaction data for each message
// Same enriched data is available for both groups and DMs
groupMessages.forEach { message ->
// Access the list of reactions
message.reactions.forEach { reaction ->
val reactionContent = reaction.content<Reaction>()
println("Reaction: ${reactionContent.content}")
}
}List messages enriched with reply metadata
Messages returned by enrichedMessages() include reply information when a message is a reply to another message.
// Retrieve messages with enriched data from a group
val groupMessages = group.enrichedMessages()
// Retrieve messages with enriched data from a DM
val dmMessages = dm.enrichedMessages()
// Access reply data for each message
// Same enriched data is available for both groups and DMs
groupMessages.forEach { message ->
// Check if the message is a reply
if (message.contentType == ContentTypeReply) {
val replyContent = message.content<Reply>()
if (replyContent != null) {
println("This is a reply to message: ${replyContent.reference}")
println("Reply text: ${replyContent.content}")
}
}
}List messages (unenriched)
Retrieve messages from a conversation.
// Get all messages
const messages = await group.messages();
// Get the 10 most recent messages
const recentMessages = await group.messages({ limit: 10n });
// Get messages after a specific time (e.g., for pagination or unread messages)
const lastCheckTimestamp = 1738620126404999936n;
const newMessages = await group.messages({
sentAfterNs: lastCheckTimestamp,
});
// Get messages from a DM conversation
const dm = await client.conversations.findOrCreateDm(recipientInboxId);
const dmMessages = await dm.messages({ limit: 50n });Count messages in a conversation
Get a count of messages without retrieving the full message list. This is more efficient when you only need the number, such as for unread message badges.
// Get total message count
const totalCount = await group.countMessages();
// Get count of messages after last check (for unread badge)
const lastCheckTimestamp = 1738620126404999936n;
const unreadCount = await group.countMessages({
sentAfterNs: lastCheckTimestamp,
});
// Get count for a DM conversation
const dm = await client.conversations.findOrCreateDm(recipientInboxId);
const dmUnreadCount = await dm.countMessages({
sentAfterNs: lastCheckTimestamp,
});Count unread messages excluding reactions
You can combine filters to count messages after the last check, excluding reactions, for example.
// Get count of unread messages, excluding reactions
const lastCheckTimestamp = 1738620126404999936n;
const unreadCount = await group.countMessages({
sentAfterNs: lastCheckTimestamp,
excludeContentTypes: [ContentType.Reaction],
});Filter messages and message counts
Apply the same filtering parameters to both retrieve filtered messages with messages() or get counts of filtered messages with countMessages().
// Using messages()
const messages = await group.messages({
sentAfterNs: 1738620126404999936n, // Filter by time
deliveryStatus: 'published', // Filter by delivery status
excludeContentTypes: [ContentType.Reaction], // Exclude content types
});
// Using countMessages()
const count = await group.countMessages({
sentAfterNs: 1738620126404999936n, // Filter by time
excludeSenderInboxIds: ['inbox_id_1'], // Exclude senders
});Filter messages by insertion time
You can filter messages using insertion time ranges with insertedAfterNs and insertedBeforeNs. This is useful when you need to query based on when messages were added to the local database rather than when they were sent.
// Get messages inserted after a specific time
const lastInsertionTime = 1738620126404999936;
const newMessages = await group.messages({
insertedAfterNs: lastInsertionTime,
sortBy: 'INSERTED',
});
// Get messages inserted within a time range
const messages = await group.messages({
insertedAfterNs: startTime,
insertedBeforeNs: endTime,
sortBy: 'INSERTED',
});Sort messages by insertion time
You can sort messages using insertedAtNs.
// Sort by insertion time for reliable pagination
const messages = await group.messages({
sortBy: "INSERTED",
limit: 20
});
// Each message has both timestamps available
messages.forEach((message) => {
console.log(`Sent at: ${message.sentAtNs}`);
console.log(`Inserted at: ${message.insertedAtNs}`);
});Paginate messages
Paginate by insertion time (recommended)
By default, messages are sorted by their sentAtNs timestamp (time when the message was sent). When you sort by sentAtNs, messages might arrive out of order. For example, a message sent 5 minutes ago might arrive in the local database after a message sent 1 minute ago. This can cause pagination issues, where you might miss messages when loading the next page.
Therefore, when paginating messages, it's more reliable to sort by insertedAtNs (time when the message was inserted into the local database). Sorting by insertedAtNs provides a totally ordered list because insertion timestamps are strictly sequential in the local database and are also deterministic and consistent.
Here's how to implement reliable message pagination using insertion time:
// First page
const firstPage = await group.messages({
limit: 20,
sortBy: 'INSERTED',
});
// Next page
const secondPage = await group.messages({
limit: 20,
insertedBeforeNs: firstPage[firstPage.length - 1].insertedAtNs,
sortBy: 'INSERTED',
});Paginate by sent time
You can also paginate using the message sent time, though this may have ordering issues if messages arrive out of sequence:
async function loadMoreMessages(
conversation: Conversation,
pageSize: number = 20
): Promise<DecodedMessage[]> {
// Get the oldest message currently loaded
const oldestMessage = currentMessages[currentMessages.length - 1];
// Load the next page of messages before the oldest one
return await conversation.messages({
limit: BigInt(pageSize),
sentBeforeNs: oldestMessage?.sentAtNs,
direction: 'descending',
});
}
// Initial load
let currentMessages = await group.messages({ limit: 20n });
// Load next page
const nextPage = await loadMoreMessages(group);
currentMessages = [...currentMessages, ...nextPage];
