The Adapter interface defines the contract for integrating new chat platforms with Chat SDK. Each platform adapter handles webhook parsing, message formatting, and platform-specific API calls.
Built-in Adapters
Chat SDK provides adapters for:
Interface Overview
interface Adapter<TThreadId = unknown, TRawMessage = unknown> {
// Properties
readonly name: string;
readonly userName: string;
readonly botUserId?: string;
// Core Methods
handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;
initialize(chat: ChatInstance): Promise<void>;
// Thread ID Management
encodeThreadId(platformData: TThreadId): string;
decodeThreadId(threadId: string): TThreadId;
channelIdFromThreadId?(threadId: string): string;
// Messaging
postMessage(threadId: string, message: AdapterPostableMessage): Promise<RawMessage<TRawMessage>>;
editMessage(threadId: string, messageId: string, message: AdapterPostableMessage): Promise<RawMessage<TRawMessage>>;
deleteMessage(threadId: string, messageId: string): Promise<void>;
postEphemeral?(threadId: string, userId: string, message: AdapterPostableMessage): Promise<EphemeralMessage>;
postChannelMessage?(channelId: string, message: AdapterPostableMessage): Promise<RawMessage<TRawMessage>>;
// Reactions
addReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
removeReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
// Fetching
fetchMessages(threadId: string, options?: FetchOptions): Promise<FetchResult<TRawMessage>>;
fetchMessage?(threadId: string, messageId: string): Promise<Message<TRawMessage> | null>;
fetchThread(threadId: string): Promise<ThreadInfo>;
fetchChannelInfo?(channelId: string): Promise<ChannelInfo>;
fetchChannelMessages?(channelId: string, options?: FetchOptions): Promise<FetchResult<TRawMessage>>;
listThreads?(channelId: string, options?: ListThreadsOptions): Promise<ListThreadsResult<TRawMessage>>;
// Formatting
parseMessage(raw: TRawMessage): Message<TRawMessage>;
renderFormatted(content: FormattedContent): string;
// Features
startTyping(threadId: string, status?: string): Promise<void>;
openDM?(userId: string): Promise<string>;
openModal?(triggerId: string, modal: ModalElement, contextId?: string): Promise<{ viewId: string }>;
stream?(threadId: string, textStream: AsyncIterable<string>, options?: StreamOptions): Promise<RawMessage<TRawMessage>>;
isDM?(threadId: string): boolean;
onThreadSubscribe?(threadId: string): Promise<void>;
}
Core Methods
name
Unique adapter name (e.g., “slack”, “teams”, “discord”)
Used as the prefix in thread IDs and for adapter identification.
userName
Bot username for this adapter
Can override the global userName from ChatConfig.
handleWebhook()
Process incoming webhooks from the platform.
Standard Web Request object
Optional webhook handling options (waitUntil for background processing)
HTTP response to return to the platform
// In your webhook endpoint
export async function POST(request: Request) {
return await chat.webhooks.slack(request);
}
initialize()
Called when the Chat instance is created. Use this to set up connections, validate credentials, etc.
async initialize(chat: ChatInstance): Promise<void> {
this.logger = chat.getLogger(this.name);
await this.validateCredentials();
}
Thread ID Management
encodeThreadId()
Convert platform-specific thread data to a string ID.
Platform-specific thread data structure
// Slack example
encodeThreadId(data: { channel: string; ts: string }): string {
return `slack:${data.channel}:${data.ts}`;
}
Thread IDs should follow the pattern: {adapter}:{channel}:{thread}
decodeThreadId()
Parse a thread ID string back to platform-specific data.
Platform-specific thread data structure
// Slack example
decodeThreadId(threadId: string): { channel: string; ts: string } {
const [, channel, ts] = threadId.split(":");
return { channel, ts };
}
channelIdFromThreadId()
Extract channel ID from a thread ID. Optional - defaults to first two parts.
channelIdFromThreadId(threadId: string): string {
const parts = threadId.split(":");
return `${parts[0]}:${parts[1]}`; // e.g., "slack:C123"
}
Messaging Methods
postMessage()
Post a message to a thread.
message
AdapterPostableMessage
required
Message content (string, markdown, AST, or card)
Platform response with message ID
editMessage()
Edit an existing message.
Thread containing the message
Platform-specific message ID
message
AdapterPostableMessage
required
New message content
deleteMessage()
Delete a message.
Thread containing the message
Platform-specific message ID
Convert mdast AST to platform-specific format.
Platform-specific formatted text
renderFormatted(ast: Root): string {
return this.formatConverter.fromAst(ast);
}
parseMessage()
Parse platform message to normalized Message object.
Platform-specific message object
parseMessage(raw: SlackMessage): Message<SlackMessage> {
return new Message({
id: raw.ts,
threadId: this.encodeThreadId({ channel: raw.channel, ts: raw.thread_ts || raw.ts }),
text: raw.text,
formatted: this.formatConverter.toAst(raw.text),
author: this.parseUser(raw.user),
metadata: { dateSent: new Date(Number(raw.ts) * 1000), edited: false },
raw,
});
}
Optional Features
stream()
Stream AI responses using platform-native streaming (Slack only).
textStream
AsyncIterable<string>
required
Text chunks to stream
Platform-specific options
Final message after streaming completes
openDM()
Open a direct message conversation with a user.
Platform-specific user ID
openModal()
Open a modal/dialog form.
Platform trigger ID from action event
Optional context ID for storing thread/message reference
Platform-specific view/dialog ID
Example: Custom Adapter
import { Adapter, Message, RawMessage, FormattedContent } from "chat";
interface MyPlatformMessage {
id: string;
channel_id: string;
thread_id?: string;
content: string;
author_id: string;
}
interface MyThreadId {
channelId: string;
threadId: string;
}
export class MyPlatformAdapter implements Adapter<MyThreadId, MyPlatformMessage> {
readonly name = "myplatform";
readonly userName: string;
constructor(userName: string) {
this.userName = userName;
}
async initialize(chat: ChatInstance): Promise<void> {
// Setup code
}
async handleWebhook(request: Request): Promise<Response> {
const payload = await request.json();
// Parse webhook and call chat.processMessage()
return new Response("OK");
}
encodeThreadId(data: MyThreadId): string {
return `myplatform:${data.channelId}:${data.threadId}`;
}
decodeThreadId(threadId: string): MyThreadId {
const [, channelId, threadId] = threadId.split(":");
return { channelId, threadId };
}
async postMessage(
threadId: string,
message: AdapterPostableMessage
): Promise<RawMessage<MyPlatformMessage>> {
const { channelId, threadId: tid } = this.decodeThreadId(threadId);
// Call platform API to post message
return { id: "msg-123", threadId, raw: {} as MyPlatformMessage };
}
parseMessage(raw: MyPlatformMessage): Message<MyPlatformMessage> {
return new Message({
id: raw.id,
threadId: this.encodeThreadId({
channelId: raw.channel_id,
threadId: raw.thread_id || raw.id,
}),
text: raw.content,
formatted: { type: "root", children: [{ type: "text", value: raw.content }] },
author: {
userId: raw.author_id,
userName: raw.author_id,
fullName: raw.author_id,
isBot: false,
isMe: false,
},
metadata: {
dateSent: new Date(),
edited: false,
},
raw,
});
}
renderFormatted(content: FormattedContent): string {
// Convert mdast to platform format
return "";
}
// Implement other required methods...
}
See Also