Initialize the Chat class with adapters, state, and configuration options.
The Chat class is the main entry point for your bot. It coordinates adapters, routes events to your handlers, and manages thread state.
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.");
});Each adapter factory auto-detects credentials from environment variables (SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, REDIS_URL, etc.), so you can get started with zero config. Pass explicit values to override.
Register multiple adapters to deploy your bot across platforms simultaneously:
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createTeamsAdapter } from "@chat-adapter/teams";
import { createDiscordAdapter } from "@chat-adapter/discord";
import { createRedisState } from "@chat-adapter/state-redis";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(),
teams: createTeamsAdapter(),
discord: createDiscordAdapter(),
},
state: createRedisState(),
});Your event handlers work identically across all registered adapters — the SDK normalizes messages, threads, and reactions into a consistent format.
| Option | Type | Default | Description |
|---|---|---|---|
userName | string | required | Default bot username across all adapters |
adapters | Record<string, Adapter> | required | Map of adapter name to adapter instance |
state | StateAdapter | required | State adapter for subscriptions and locking |
logger | Logger | LogLevel | "info" | Logger instance or log level ("debug", "info", "warn", "error", "silent") |
dedupeTtlMs | number | 300000 | TTL in ms for message deduplication (5 minutes) |
concurrency | "drop" | "queue" | "debounce" | "burst" | "concurrent" | ConcurrencyConfig | "drop" | Strategy for overlapping messages on the same thread |
streamingUpdateIntervalMs | number | 500 | Update interval in ms for post+edit streaming |
fallbackStreamingPlaceholderText | string | null | "..." | Placeholder text while streaming starts. Set to null to skip |
onLockConflict | 'drop' | 'force' | (threadId, message) => 'drop' | 'force' | "drop" | Behavior when a thread lock is already held. 'force' releases the existing lock and re-acquires it, enabling interrupt/steerability for long-running handlers |
Use getAdapter to access platform-specific APIs when you need functionality beyond the unified interface:
import type { SlackAdapter } from "@chat-adapter/slack";
const slack = bot.getAdapter("slack") as SlackAdapter;
await slack.setSuggestedPrompts(channelId, threadTs, [
{ title: "Get started", message: "What can you help me with?" },
]);For typed access to the platform's native API client, use the SDK-named getter on each adapter:
const slack = bot.getAdapter("slack").webClient; // WebClient
const linear = bot.getAdapter("linear").linearClient; // LinearClient
const github = bot.getAdapter("github").octokit; // OctokitThe previous .client getter still works as a deprecated alias on all three adapters.
See getAdapter for multi-tenant constraints.
The webhooks property provides type-safe handlers for each registered adapter. Wire these up to your HTTP framework's routes:
import { bot } from "@/lib/bot";
export const POST = bot.webhooks.slack;import { bot } from "@/lib/bot";
export const POST = bot.webhooks.teams;The Chat instance initializes lazily on the first webhook. You can also initialize manually:
For graceful shutdown (e.g. in serverless teardown), call shutdown:
Register a singleton when you need to access the Chat instance from multiple files:
const bot = new Chat({ /* ...config */ }).registerSingleton();
export default bot;import { Chat } from "chat";
const bot = Chat.getSingleton();Open a DM thread with a user by passing their platform user ID or an Author object:
const dm = await bot.openDM("U123ABC");
await dm.post("Hey! Just wanted to follow up on your request.");Get a channel directly by its ID:
const channel = bot.channel("slack:C123ABC");
await channel.post("Announcement: deploy complete!");










