channel docs

channel / guides / platform-setup

Platform setup.

Each platform follows the same recipe: install its adapter package (an optional peer — you only carry what you use), collect credentials from the platform's app settings, and pass them through in platforms. The config object for each platform is a passthrough: whatever the platform needs, the runtime forwards.

Slack

terminal
pnpm add @chat-adapter/slack

Create an app at api.slack.com/apps, add a bot user, install it to your workspace, and enable Event Subscriptions for message events. You need the bot token (starts with xoxb-, under OAuth & Permissions) and the signing secret (under Basic Information) — the runtime uses it to verify every webhook.

.env.local
SLACK_BOT_TOKEN=xoxb-...
SLACK_SIGNING_SECRET=...
lib/channels.ts
platforms: {
  slack: {
    botToken: process.env.SLACK_BOT_TOKEN!,
    signingSecret: process.env.SLACK_SIGNING_SECRET!,
  },
},

Microsoft Teams

terminal
pnpm add @chat-adapter/teams

Teams bots are registered through an Azure Bot resource. You need the bot's app id and app password (client secret) from the bot registration — consult your Azure bot resource settings for the exact values your tenant setup requires.

.env.local
TEAMS_APP_ID=...
TEAMS_APP_PASSWORD=...
lib/channels.ts
platforms: {
  teams: {
    appId: process.env.TEAMS_APP_ID!,
    appPassword: process.env.TEAMS_APP_PASSWORD!,
  },
},

Google Chat

terminal
pnpm add @chat-adapter/gchat

Google Chat apps authenticate with a service account: enable the Chat API in Google Cloud Console, configure the app under Chat API → Configuration, and create a service account key. Consult your Google Cloud project settings for the credential shape your deployment uses (a JSON key is the common path).

.env.local
GCHAT_SERVICE_ACCOUNT_KEY={"type":"service_account",...}
lib/channels.ts
platforms: {
  gchat: {
    serviceAccountKey: JSON.parse(process.env.GCHAT_SERVICE_ACCOUNT_KEY!),
  },
},

Discord

terminal
pnpm add @chat-adapter/discord

Create an application in the Discord Developer Portal and add a bot to it. You need the bot token (under Bot) and the public key (under General Information) — the public key is what verifies signed webhook requests. Invite the bot to your server with the message scopes your use case needs.

.env.local
DISCORD_BOT_TOKEN=...
DISCORD_PUBLIC_KEY=...
lib/channels.ts
platforms: {
  discord: {
    botToken: process.env.DISCORD_BOT_TOKEN!,
    publicKey: process.env.DISCORD_PUBLIC_KEY!,
  },
},

Telegram

terminal
pnpm add @chat-adapter/telegram

The simplest of the five: message @BotFather on Telegram, create a bot, and copy the bot token it gives you. That single token covers both sending and webhook verification.

.env.local
TELEGRAM_BOT_TOKEN=123456:ABC-...
lib/channels.ts
platforms: {
  telegram: { botToken: process.env.TELEGRAM_BOT_TOKEN! },
},

Everything together

Enable only what you use — each key in platforms produces one webhook handler in channels.webhooks and nothing else changes:

lib/channels.ts
export const channels = await createChannels({
  db,
  userName: "ekairos",
  platforms: {
    slack: {
      botToken: process.env.SLACK_BOT_TOKEN!,
      signingSecret: process.env.SLACK_SIGNING_SECRET!,
    },
    telegram: { botToken: process.env.TELEGRAM_BOT_TOKEN! },
    // teams, gchat, discord: same pattern, when you need them
  },
  resolveContextId,
  react,
});
Exact credential field names follow each adapter package's config — when in doubt, consult your platform's app settings and the adapter's own documentation. The platforms object forwards your config verbatim, so there is no ekairos-specific translation layer to learn.

Under the hood

Platform delivery is built on Vercel's Chat SDK, wrapped and contained inside @ekairos/channel — its platform adapters are the @chat-adapter/* packages you install above, and they are wrapped too. Nothing of it leaks into your app: you talk to the channel domain (createChannels, canonical channel_messages), and all conversation state lives in InstantDB rather than in the delivery layer. Upstream improvements reach you as plain dependency bumps — no integration code to rewrite.

Next

database…