Skip to content

Threads & Replies

Threads allow users to reply to specific messages without cluttering the main channel conversation. A thread is created when a message is sent with a parent_id referencing another message.

Starting a Thread

Send a message with a parent_id to start a thread or add a reply to an existing thread.

final reply = await channel.sendMessage(
  Message(
    text: 'This is a reply in a thread',
    parentId: parentMessageId,
    showInChannel: false,
  ),
);

Thread Parameters

Name Type Description Default Optional
parent_id string ID of the parent message to reply to
show_in_channel boolean If true, the reply appears both in the thread and the main channel false
Info:

Messages in threads support the same features as regular messages: reactions, attachments, and mentions.

Paginating Thread Replies

When querying a channel, thread replies are not included by default. The parent message includes a reply_count field. Use getReplies to fetch thread messages.

// Get the first 20 replies
final replies = await channel.getReplies(
  parentMessageId,
  options: PaginationParams(limit: 20),
);

// Get older replies
final olderReplies = await channel.getReplies(
  parentMessageId,
  options: PaginationParams(limit: 20, lessThanOrEqual: '42'),
);

Inline Replies

Reply to a message inline without creating a thread. The referenced message appears within the new message. Use quoted_message_id instead of parent_id.

final message = await channel.sendMessage(Message(
  text: 'I agree with this point',
  quotedMessageId: originalMessageId,
));

When querying messages, the quoted_message field is automatically populated:

{
  "id": "new-message-id",
  "text": "I agree with this point",
  "quoted_message_id": "original-message-id",
  "quoted_message": {
    "id": "original-message-id",
    "text": "The original message text"
  }
}
Warning:

Inline replies are only available one level deep. If Message A replies to Message B, and Message B replies to Message C, you cannot access Message C through Message A. Fetch Message B directly to access its referenced message.

Thread List

Query all threads that the current user participates in. This is useful for building thread list views similar to Slack or Discord.

Querying Threads

Threads are returned with unread replies first, sorted by the latest reply timestamp in descending order.

final response = await client.queryThreads();

for (final thread in response.threads) {
  print(thread.parentMessage?.text);
  print(thread.latestReplies);
  print(thread.threadParticipants);
  print(thread.read);
}

Query Options

Name Type Description Default Optional
reply_limit number Number of latest replies to fetch per thread 2
participant_limit number Number of thread participants to fetch per thread 100
limit number Maximum number of threads to return 10
watch boolean If true, watch channels for the returned threads true
member_limit number Number of members to fetch per thread channel 100

Filtering and Sorting

Filter and sort threads using MongoDB-style query operators.

Supported Filter Fields

Field Type Operators Description
channel_cid string or list of strings $eq, $in Channel CID
channel.disabled boolean $eq Channel disabled status
channel.team string or list of strings $eq, $in Channel team
parent_message_id string or list of strings $eq, $in Parent message ID
created_by_user_id string or list of strings $eq, $in Thread creator's user ID
created_at string (RFC3339) $eq, $gt, $lt, $gte, $lte Thread creation timestamp
updated_at string (RFC3339) $eq, $gt, $lt, $gte, $lte Thread update timestamp
last_message_at string (RFC3339) $eq, $gt, $lt, $gte, $lte Last message timestamp

Supported Sort Fields

  • active_participant_count
  • created_at
  • last_message_at
  • parent_message_id
  • participant_count
  • reply_count
  • updated_at

Use 1 for ascending order and -1 for descending order.

final filter = Filter.and([
  Filter.equal('created_by_user_id', 'user-1'),
  Filter.greaterOrEqual('updated_at', '2024-01-01T00:00:00Z'),
]);
final sort = [SortOption<Thread>.desc('created_at')];

final response = await client.queryThreads(
  filter: filter,
  sort: sort,
  pagination: PaginationParams(limit: 10),
);

// Get next page
if (response.next != null) {
  final nextPage = await client.queryThreads(
    filter: filter,
    sort: sort,
    pagination: PaginationParams(limit: 10, next: response.next),
  );
}

Getting a Thread by ID

Retrieve a specific thread using the parent message ID.

final response = await client.getThread(
  parentMessageId,
  options: ThreadOptions(
    watch: true,
    replyLimit: 10,
    participantLimit: 25,
  ),
);
final thread = response.thread;

Updating Thread Title and Custom Data

Assign a title and custom data to a thread.

// Set properties
final response = await client.partialUpdateThread(
  threadId,
  set: {
    'title': 'Project Discussion',
    'priority': 'high',
  },
);

// Remove properties
await client.partialUpdateThread(
  threadId,
  unset: ['priority'],
);

Thread Unread Counts

Total Unread Threads

The total number of unread threads is available after connecting.

final user = await client.connectUser(User(id: 'user-id'), token);
print(user.unreadThreads);

// Or read it later from the cached current user
print(client.state.currentUser?.unreadThreads);

Marking Threads as Read or Unread

// Mark thread as read
await channel.markThreadRead(parentMessageId);

// Mark thread as unread
await channel.markThreadUnread(parentMessageId);

Unread Count Per Thread

final response = await client.getUnreadCount();

print(response.totalUnreadThreadsCount);

for (final thread in response.threads) {
  print(thread.parentMessageId);
  print(thread.unreadCount);
  print(thread.lastRead);
}

Thread Manager

The ThreadManager class provides built-in pagination and state management for threads.

JavaScript
// Access the client's thread manager
const threadManager = client.threads;

// Subscribe to state updates
const unsubscribe = threadManager.state.subscribe((state) => {
  console.log(state.threads);
  console.log(state.unreadThreadCount);
});

// Load threads
await threadManager.loadNextPage();

// Access current state
const { threads } = threadManager.state.getLatestValue();

Event Handling

Register subscriptions to receive real-time updates for threads.

const { threads } = await client.queryThreads({ watch: true, limit: 10 });
const [thread] = threads;

// Register event handlers for a single thread
thread.registerSubscriptions();

const unsubscribe = thread.state.subscribe((state) => {
  console.log(state.replies);
});
Info:

The watch parameter is required when querying threads to receive real-time updates.

For ThreadManager, call registerSubscriptions once to automatically manage subscriptions for all loaded threads:

const threadManager = client.threads;
threadManager.registerSubscriptions();

await threadManager.loadNextPage();

// All threads are now listening to channel events
const { threads } = threadManager.state.getLatestValue();