Platform Adapters

Platform-specific adapters that connect your bot to any messaging platform.

Platform adapters handle webhook verification, message parsing, and API calls for each messaging platform. Install only the adapters you need. Browse all available adapters, including community-built ones, on the Adapters page.

Need a browser chat UI? See the Web adapter. It speaks the AI SDK UI stream protocol and works with React (@ai-sdk/react), Vue (@ai-sdk/vue), and Svelte (@ai-sdk/svelte), so the same bot serves Slack, Teams, and any browser framework out of the box.

Feature matrix

This matrix covers Vercel-maintained official adapters only. For vendor-official and community adapters, see each adapter's page.

Messaging

FeatureDiscordGoogle ChatGitHubInstagramLinearMessengerNotionSlackMicrosoft TeamsTelegramTwilioWebWhatsApp Business CloudXXChat
Post message
Message repliesOutbound only
Edit messagePartialPosts onlyOwn text messages, age-gated
Delete messagePartialOwn messages
File uploadsUploads and HTTPS URLsUp to 3 native attachmentsPublic media URLsImages, audio, docs
StreamingPost+EditPost+EditBufferedBufferedAgent sessions / Post+EditBufferedPost+EditNativeNative (DMs) / BufferedPost+Edit / Opt-in rich draftsBufferedNative (SSE)BufferedBufferedAge-gated edits
Scheduled messagesNative

Rich content

FeatureDiscordGoogle ChatGitHubInstagramLinearMessengerNotionSlackMicrosoft TeamsTelegramTwilioWebWhatsApp Business CloudXXChat
Card formatEmbeds / ComponentsGoogle Chat CardsGFM MarkdownGeneric / Button TemplatesMarkdownGeneric / Button TemplatesMarkdown fallbackBlock KitAdaptive CardsMarkdownV2 + inline keyboardPlain text fallbackMarkdown only (v1)Interactive messagesPlain textPlain text + URL preview
ButtonsQuick replies / postbacksMax 3, postbackInline keyboardInteractive replies
Link buttonsweb_urlweb_urlInline keyboard URLsSingle CTA URLRendered as textTappable URLs
Select menusComponents
TablesGFMASCIIGFMGFMASCIIFlattened markdownBlock KitGFMNative messages / ASCII cardsASCIIGFMASCIIASCII
ChartsBlock Kit
FieldsASCIIFormatted textPlain textPlain text
Images in cards
Modals

Conversations

FeatureDiscordGoogle ChatGitHubInstagramLinearMessengerNotionSlackMicrosoft TeamsTelegramTwilioWebWhatsApp Business CloudXXChat
Slash commands
Mentions
Add reactionsWorkspace EventsLikes only
Remove reactionsWorkspace EventsPartialPartialLikes only
Typing indicatorAgent sessions
Mark as read
Message edit events
Message delete events
DMsRequires delegation
Ephemeral messagesNativeNativeTargeted messages
User lookupCachedCachedSeen users
Parent subject
Native client
Custom API endpointMedia REST calls

Message history

FeatureDiscordGoogle ChatGitHubInstagramLinearMessengerNotionSlackMicrosoft TeamsTelegramTwilioWebWhatsApp Business CloudXXChat
Fetch messagesRequires delegationCached sent onlyCached sent onlyRequires Graph permissionsCachedMessages APIState cacheCached sent onlyDMs via API, posts cached
Fetch single messageCachedCachedCachedPosts via API, DMs cached
Fetch thread infoSynthesized
Fetch channel messagesCachedCachedRequires Graph permissionsCachedState cache
List threadsRequires Graph permissions
Fetch channel infoRequires Graph permissions
Post channel message

Partial support means the feature works with limitations. See individual adapter pages for details.

How adapters work

Each adapter implements a standard interface that the Chat class uses to route events and send messages. When a webhook arrives:

  1. The adapter verifies the request signature
  2. Parses the platform-specific payload into a normalized Message
  3. Routes to your handlers via the Chat class
  4. Converts outgoing messages from markdown/AST/cards to the platform's native format

Using multiple adapters

Register multiple adapters and your event handlers work across all of them:

lib/bot.ts
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createTeamsAdapter } from "@chat-adapter/teams";
import { createGoogleChatAdapter } from "@chat-adapter/gchat";
import { createRedisState } from "@chat-adapter/state-redis";

const bot = new Chat({
  userName: "mybot",
  adapters: {
    slack: createSlackAdapter(),
    teams: createTeamsAdapter(),
    gchat: createGoogleChatAdapter(),
  },
  state: createRedisState(),
});

// This handler fires for mentions on any platform
bot.onNewMention(async (thread) => {
  await thread.subscribe();
  await thread.post("Hello!");
});

Each adapter auto-detects credentials from environment variables, so you only need to pass config when overriding defaults.

The examples above use Redis for state. See State Adapters for all available options.

Each adapter creates a webhook handler accessible via bot.webhooks.<name>.

Customizing an adapter via subclassing

Each official adapter exposes its extension surface as protected members so you can subclass it to override or extend platform-specific behavior without forking the package. Use this when you need to handle a payload type the built-in adapter doesn't cover, intercept verification, or wrap an existing handler.

lib/custom-telegram.ts
import { TelegramAdapter, type TelegramUpdate } from "@chat-adapter/telegram";
import type { WebhookOptions } from "chat";

export class CustomTelegramAdapter extends TelegramAdapter {
  protected override processUpdate(
    update: TelegramUpdate,
    options?: WebhookOptions
  ): void {
    // Handle a payload type the base adapter doesn't, e.g. chat_join_request.
    if ("chat_join_request" in update) {
      this.logger.info("Received chat_join_request", { update });
      return;
    }
    super.processUpdate(update, options);
  }
}

Construct your subclass anywhere you'd construct the base adapter, for example, adapters: { telegram: new CustomTelegramAdapter({ ... }) }. Members marked private intentionally remain inaccessible. If you find a hook you need that isn't protected, please open an issue.

The protected extension surface is intentionally broader than the public API but is not yet considered fully stable. Method signatures may evolve in minor releases as we learn from real-world subclasses. Pin the adapter version you build against, watch the changelog for the affected adapter, and prefer overriding the smallest hook that solves your problem so upgrades stay easy. If you rely on a particular hook, please open an issue so we can promote it to a stable, documented extension point.

Read more