Back to Copilotkit

Channels Overview

showcase/shell-docs/src/content/docs/channels/index.mdx

1.64.15.9 KB
Original Source

Channels let you take a CopilotKit agent and run it as a bot inside a chat app. The user talks to your agent in Slack, Teams, WhatsApp, Telegram, or Discord, and your agent answers right there in the thread.

You write the bot once. The same agent, tools, commands, and interactive cards run on every platform you attach.

A channel, end to end

Three pieces: a TanStack AI agent, JSX turned on, and a channel that plugs the agent into a platform and replies with clickable UI.

bash
npm install @copilotkit/channels @copilotkit/runtime @tanstack/ai @tanstack/ai-openai
<Steps> <Step> ### The agent

A Built-in Agent in TanStack factory mode. You own the model call with TanStack AI; the agent turns its stream into the events the channel renders.

ts
import { BuiltInAgent, convertInputToTanStackAI } from "@copilotkit/runtime/v2";
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";

export const agent = new BuiltInAgent({
  type: "tanstack",
  factory: (ctx) => {
    const { messages, systemPrompts, tools } = convertInputToTanStackAI(ctx.input);
    return chat({
      adapter: openaiText("gpt-5.5"),
      messages,
      systemPrompts: ["You are a helpful support assistant.", ...systemPrompts],
      tools,
      abortController: ctx.abortController,
    });
  },
});
</Step> <Step> ### Turn on JSX

Replies render as native UI. Point the JSX runtime at the channels package.

jsonc
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@copilotkit/channels"
  }
}
</Step> <Step> ### The channel

Declare a channel, then answer messages. A reply can be the agent's own words, or a card whose button you handle right where you wrote it.

tsx
import { createChannel } from "@copilotkit/channels";
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
import { Message, Section, Actions, Button } from "@copilotkit/channels/ui";
import { agent } from "./agent";

// A managed channel: Intelligence hosts the transport, so no adapter.
const channel = createChannel({
  name: "support-bot",
  agent,
});

channel.onMessage(async ({ thread, message }) => {
  // let the agent answer
  await thread.runAgent({ prompt: message.text });

  // then post a card, and handle the click right here
  await thread.post(
    <Message>
      <Section>{"Did that answer your question?"}</Section>
      <Actions>
        <Button 
          onClick={async ({ thread }) => {
            await thread.post("Glad I could help!");
          }}
        >
          Yes, thanks
        </Button>
      </Actions>
    </Message>,
  );
});

// The runtime owns the channel's lifecycle. A CopilotKit Intelligence key runs
// any channel (free tier available); `ready()` activates it.
const runtime = new CopilotRuntime({
  agents: {},
  intelligence: new CopilotKitIntelligence({
    // apiUrl and wsUrl default to the managed Intelligence platform. Override
    // both together only for a self-hosted deployment.
    apiKey: process.env.COPILOTKIT_API_KEY!,
  }),
  identifyUser: () => ({ id: "demo-user", name: "Demo User" }),
  channels: [channel],
});

const listener = createCopilotNodeListener({ runtime });
await listener.channels.ready();
// Mount the listener on an HTTP server to keep the process running (see the Quickstart).
</Step> </Steps>

That is a working bot: a TanStack AI agent, answering across platforms, replying with clickable UI. The Quickstart adds the dashboard setup and environment, and the UI library shows how to register that card so its button still works after a restart.

Two ways to connect a platform

Every channel runs through the Intelligence runtime, so both paths need a CopilotKit Intelligence key (free tier available). The difference is who holds the platform bot credentials — either way, the runtime drives the channel.

ManagedDirect (platform adapter)
Who holds the platform tokensCopilotKit IntelligenceYou supply them to the adapter
Add a platformAttach it in the Intelligence dashboardAdd another adapter in code
Best forShipping fast, many platforms, no platform secrets in your appOwning one platform's connection end to end

With the managed path your process holds no Slack or Teams tokens: Intelligence receives the platform event, delivers the turn to your bot, runs the same handlers, and does the credentialed send back. With a direct adapter you supply the platform credentials, but the runtime still owns the channel's lifecycle. Start with managed. See Platforms for what Intelligence handles for you and the per-platform setup.

What you can build

<Cards> <Card title="Quickstart" href="/channels/quickstart" description="A working bot that answers messages, end to end." /> <Card title="UI library" href="/channels/ui-library" description="Reply with cards, buttons, and inputs using JSX." /> <Card title="Interactive flows" href="/channels/interactive" description="Approval gates and reaction handlers built on components." /> <Card title="Commands and reactions" href="/channels/commands-and-reactions" description="Slash commands and react-to-any-message handlers." /> <Card title="Files and multimodality" href="/channels/files-and-multimodality" description="Let users drop in images and files for the agent to read." /> <Card title="MCP servers" href="/channels/mcp" description="Give the bot external tools through MCP." /> <Card title="Platforms" href="/channels/platforms" description="Teams, WhatsApp, Slack, Telegram, and Discord, and what Intelligence handles for you." /> <Card title="API reference" href="/channels/reference/channel" description="Every method on the channel, the thread, and JSX callbacks." /> </Cards>