Looking for the chatbot template? It's now here.

Thread

Represents a conversation thread with methods for posting, subscribing, and state management.

A Thread is provided to your event handlers and represents a conversation thread on any platform. You don't create threads directly — they come from handler callbacks or chat.openDM().

Properties

Prop

Type

post

Post a message to the thread. Accepts strings, structured messages, cards, and streams.

// Plain text
await thread.post("Hello!");

// Markdown
await thread.post({ markdown: "**Bold** text" });

// AST
await thread.post({ ast: root([paragraph([text("Hello")])]) });

// Card
await thread.post(Card({ title: "Hi", children: [Text("Hello")] }));

// Stream
await thread.post(result.textStream);

Parameters: message: string | PostableMessage | CardJSXElement

Returns: Promise<SentMessage> — the sent message with edit(), delete(), addReaction(), and removeReaction() methods.

See Posting Messages for details on each format.

postEphemeral

Post a message visible only to a specific user.

await thread.postEphemeral(userId, "Only you can see this", {
  fallbackToDM: true,
});

Prop

Type

Returns: Promise<EphemeralMessage | null>

subscribe / unsubscribe

Manage thread subscriptions. Subscribed threads route all messages to onSubscribedMessage handlers.

await thread.subscribe();
await thread.unsubscribe();
const subscribed = await thread.isSubscribed();

Subscriptions persist across restarts via your state adapter.

state

Store typed, per-thread state that persists across requests. State has a 30-day TTL.

// Read state
const state = await thread.state; // TState | null

// Merge into existing state
await thread.setState({ aiMode: true });

// Replace state entirely
await thread.setState({ aiMode: false }, { replace: true });

startTyping

Show a typing indicator in the thread. No-op on platforms that don't support it.

await thread.startTyping();

messages / allMessages

Iterate through message history.

// Newest first (auto-paginates)
for await (const msg of thread.messages) {
  console.log(msg.text);
}

// Oldest first (auto-paginates)
for await (const msg of thread.allMessages) {
  console.log(msg.text);
}

refresh

Re-fetch messages from the API and update recentMessages.

await thread.refresh();

mentionUser

Get a platform-specific @-mention string for a user.

await thread.post(`Hey ${thread.mentionUser(userId)}, check this out!`);

Serialization

Threads can be serialized for workflow engines and external systems. The serialized thread includes the current message if one is available.

// Serialize
const json = thread.toJSON();

// Pass to a workflow
await workflow.start("my-workflow", {
  thread: thread.toJSON(),
});

The serialized format includes the thread ID, channel ID, adapter name, DM status, and the current message (if present).

Deserialization

Use bot.reviver() as a JSON.parse reviver to automatically restore Thread and Message objects from serialized payloads:

const data = JSON.parse(payload, bot.reviver());
await data.thread.post("Hello from workflow!");

Under the hood, the reviver calls ThreadImpl.fromJSON() and Message.fromJSON() for any serialized objects it encounters.

SentMessage

Returned by thread.post(). Extends Message with mutation methods.

Prop

Type