One bot, every workplace chat
An internal challenge at Vercel—multiply your output by building agents—revealed a bottleneck fast. Teams were creating useful, purpose-built chat bots, but each one needed its own custom integration for every platform people wanted to use it on. Slack came first, then Discord, GitHub, Linear, and the integration work multiplied with each new destination.
The fix wasn't more integration code. It was an abstraction layer that treats chat platforms the way the AI SDK treats model providers. The Chat SDK is a TypeScript library that routes events and application logic through a single core chat package, with platform-specific adapters handling the differences. Write your bot logic once; swap the adapter when you change deployment targets from Slack to Teams to Discord.
import { streamText } from "ai";
const result = await streamText({
model: "anthropic/claude-opus-4.6", // swap out the provider
prompt: "Hello world",
});
A basic bot is just a handler function and an adapter. Adapters auto-detect credentials from environment variables, so there is no setup ceremony between you and a working test deployment.
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createRedisState } from "@chat-adapter/state-redis";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(),
},
state: createRedisState(),
});
bot.onNewMention(async (thread) => {
await thread.subscribe();
await thread.post("Hello! I'm listening to this thread now.");
});
bot.onSubscribedMessage(async (thread, message) => {
await thread.post(`You said: ${message.text}`);
});
Letting adapters take the inconsistencies
Chat platforms disagree on almost everything, from streaming to formatting to interactivity. Rather than hiding those differences behind a lowest-common-denominator API, the SDK resolves them inside the adapter layer. Your application code stays uniform.
Streaming is a good example. Slack has a native append-only streaming path that renders bold, italic, and lists in real time. Other platforms don't, so they fall back to a path where streamed text passes through each adapter's markdown-to-native conversion pipeline at every intermediate edit. Before this SDK, those fallback adapters received raw markdown strings—users on Discord or Teams would see literal **bold** syntax until the final message resolved. Now conversion happens automatically as chunks arrive.
Tables follow the same pattern. The Table() component gives you a composable, adapter-aware API: pass headers and rows and the platform decides the format. Slack renders Block Kit table blocks; Teams and Discord use GFM markdown tables; Google Chat gets monospace text widgets; Telegram receives code blocks; GitHub and Linear reuse their markdown pipelines. The same philosophy extends to cards, modals, and buttons—JSX elements that each adapter renders natively, gracefully falling back when a platform lacks support.
import { Table } from "chat";
await thread.post(
<Table
headers={["Name", "Status", "Region"]}
rows={[
["api-prod", "healthy", "iad1"],
["api-staging", "degraded", "sfo1"],
]}
/>
);
Why one platform still benefits
Even a Slack-only bot gets meaningful value. The SDK converts channel and user names to clear text in both directions, so your agent sees readable conversational context and its at-mentions actually trigger notifications when it responds. Link previews, referenced posts, and images are automatically pulled into agent prompts for richer context.
Markdown conversion is another hidden win. Models generate standard markdown, which Slack does not accept natively; the SDK converts it to Slack's variant on the fly, even mid-stream on the native append-only API. The post() function accepts an AI SDK text stream directly, so piping model output to any chat platform takes no additional wiring.
import { streamText } from "ai";
bot.onNewMention(async (thread) => {
await thread.subscribe();
const result = await streamText({
model: "anthropic/claude-sonnet-4",
prompt: "Summarize what's happening in this thread.",
});
await thread.post(result.textStream);
});
State beyond single-process memory
Thread subscriptions, distributed locks, and key-value cache state are pluggable. Redis and ioredis adapters shipped at launch, and PostgreSQL support arrived as a production-ready option for teams that don't want to introduce a new datastore. The PostgreSQL adapter sits on pg (node-postgres) with raw SQL, creates the required tables on first connect, and supports TTL-based caching, cross-instance distributed locking, and key-prefix namespacing.
import { createPostgresState } from "@chat-adapter/state-postgres";
import { createSlackAdapter } from "@chat-adapter/slack";
import { Chat } from "chat";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(),
},
state: createPostgresState(),
});
WhatsApp on board
WhatsApp support extends the write-once model to one of the largest messaging platforms. The adapter handles messages, reactions, auto-chunking, read receipts, and multi-media downloads—images, voice messages, stickers—plus location sharing via Google Maps URLs. Cards map to interactive reply buttons with up to three options, falling back to formatted text where needed. The constraints are WhatsApp's own: a strict 24-hour messaging window, and no support for message history, editing, or deletion.
import { createWhatsAppAdapter } from "@chat-adapter/whatsapp";
import { Chat } from "chat";
const bot = new Chat({
userName: "mybot",
adapters: {
whatsapp: createWhatsAppAdapter(),
},
state: createRedisState(),
});
bot.onNewMention(async (thread) => {
await thread.post("Hello from WhatsApp!");
});
Taking it to your agents
For teams already building with AI agents, a Chat skill is available to install directly:
npx skills add vercel/chat
That command gives your coding agents access to the SDK's documentation, patterns, and best practices. A starter prompt is also included for migrating an existing multi-platform bot to the unified architecture.
Chat SDK is open source and in public beta, with documentation covering setup, adapter configuration, and state, plus a resources library of templates and guides. The agents your team has built don't have to live on one platform; they can be deployed where your users already are.



