Skip to main content
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

name
string
required
Unique adapter name (e.g., “slack”, “teams”, “discord”)
Used as the prefix in thread IDs and for adapter identification.

userName

userName
string
required
Bot username for this adapter
Can override the global userName from ChatConfig.

handleWebhook()

Process incoming webhooks from the platform.
request
Request
required
Standard Web Request object
options
WebhookOptions
Optional webhook handling options (waitUntil for background processing)
response
Response
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.
chat
ChatInstance
required
The Chat instance
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.
platformData
TThreadId
required
Platform-specific thread data structure
threadId
string
Encoded thread ID string
// 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.
threadId
string
required
Thread ID string
platformData
TThreadId
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.
threadId
string
required
Thread ID string
channelId
string
Channel ID
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.
threadId
string
required
Thread ID to post to
message
AdapterPostableMessage
required
Message content (string, markdown, AST, or card)
rawMessage
RawMessage<TRawMessage>
Platform response with message ID

editMessage()

Edit an existing message.
threadId
string
required
Thread containing the message
messageId
string
required
Platform-specific message ID
message
AdapterPostableMessage
required
New message content
rawMessage
RawMessage<TRawMessage>
Updated message

deleteMessage()

Delete a message.
threadId
string
required
Thread containing the message
messageId
string
required
Platform-specific message ID

renderFormatted()

Convert mdast AST to platform-specific format.
content
FormattedContent
required
mdast Root node
rendered
string
Platform-specific formatted text
renderFormatted(ast: Root): string {
  return this.formatConverter.fromAst(ast);
}

parseMessage()

Parse platform message to normalized Message object.
raw
TRawMessage
required
Platform-specific message object
message
Message<TRawMessage>
Normalized message
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).
threadId
string
required
Thread to stream to
textStream
AsyncIterable<string>
required
Text chunks to stream
options
StreamOptions
Platform-specific options
rawMessage
RawMessage<TRawMessage>
Final message after streaming completes

openDM()

Open a direct message conversation with a user.
userId
string
required
Platform-specific user ID
threadId
string
Thread ID for the DM

openModal()

Open a modal/dialog form.
triggerId
string
required
Platform trigger ID from action event
modal
ModalElement
required
Modal element to display
contextId
string
Optional context ID for storing thread/message reference
result
{ viewId: string }
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