Multi-Channel Support
FlowKit supports deploying your conversational flows across multiple messaging platforms with platform-specific formatting.
Overview
Channel adapters handle:
- Message formatting for each platform
- Rich content (buttons, images, cards)
- Platform-specific features
- Sending/receiving messages
Available Adapters
| Adapter | Import Name | Features |
|---|---|---|
| Web | WebChannelAdapter | Buttons, images, cards |
WhatsAppAdapter | Buttons, images, typing | |
| Telegram | TelegramAdapter | Inline keyboards, images |
| Slack | SlackAdapter | Block Kit, buttons |
Basic Usage
typescript
import {
ChannelManager,
WebChannelAdapter,
WhatsAppAdapter,
TelegramAdapter,
SlackAdapter,
} from "@andresaya/flowkit";ChannelManager
The ChannelManager coordinates multiple channel adapters:
typescript
import { ChannelManager, WhatsAppAdapter, TelegramAdapter } from "@andresaya/flowkit";
const manager = new ChannelManager();
// Register adapters
manager.register(new WhatsAppAdapter({
accessToken: process.env.WHATSAPP_TOKEN!,
phoneNumberId: process.env.WHATSAPP_PHONE_ID!,
}));
manager.register(new TelegramAdapter({
botToken: process.env.TELEGRAM_BOT_TOKEN!,
}));
// Set message handler
manager.onMessage(async (message) => {
const result = await engine.handle(message.senderId, message.text);
return { text: result.message };
});
// Handle incoming webhooks
app.post("/webhook/:channel", async (req, res) => {
const channel = req.params.channel as ChannelType;
const result = await manager.handleWebhook(channel, req.body);
res.json(result);
});WebChannelAdapter
For web-based chat widgets and custom frontends.
typescript
import { WebChannelAdapter } from "@andresaya/flowkit";
const web = new WebChannelAdapter();
// Parse incoming message
const message = web.parseMessage({
id: "msg-123",
senderId: "user-456",
text: "Hello!",
});
// Format response
const formatted = web.formatResponse({
text: "How can I help you?",
buttons: [
{ type: "postback", title: "Sales", payload: "sales" },
{ type: "postback", title: "Support", payload: "support" },
],
});Socket.io Integration
typescript
import { Server } from "socket.io";
import { WebChannelAdapter, FlowEngine } from "@andresaya/flowkit";
const io = new Server(server);
const web = new WebChannelAdapter();
io.on("connection", (socket) => {
const sessionId = `web:${socket.id}`;
// Start conversation
engine.start(sessionId).then((result) => {
socket.emit("message", web.formatResponse({ text: result.message }));
});
// Handle messages
socket.on("message", async (text: string) => {
const result = await engine.handle(sessionId, text);
socket.emit("message", web.formatResponse({ text: result.message }));
});
});WhatsAppAdapter
Integration with WhatsApp Business API (Cloud API).
typescript
import { WhatsAppAdapter } from "@andresaya/flowkit";
const whatsapp = new WhatsAppAdapter({
accessToken: process.env.WHATSAPP_TOKEN!,
phoneNumberId: process.env.WHATSAPP_PHONE_ID!,
verifyToken: process.env.WHATSAPP_VERIFY_TOKEN!, // For webhook verification
});
// Send a message
await whatsapp.send("+1234567890", {
text: "Hello! How can I help you?",
buttons: [
{ type: "postback", title: "📦 Track Order", payload: "track" },
{ type: "postback", title: "💬 Support", payload: "support" },
],
});WhatsApp Webhook
typescript
import express from "express";
const app = express();
// Webhook verification (GET)
app.get("/webhook/whatsapp", (req, res) => {
const result = whatsapp.verifyWebhook(req.query);
if (result?.valid) {
res.send(result.challenge);
} else {
res.sendStatus(403);
}
});
// Handle messages (POST)
app.post("/webhook/whatsapp", express.json(), async (req, res) => {
const message = whatsapp.parseMessage(req.body);
if (message) {
const result = await engine.handle(
whatsapp.getConversationId(message),
message.text
);
await whatsapp.send(message.senderId, { text: result.message });
}
res.sendStatus(200);
});TelegramAdapter
Integration with Telegram Bot API.
typescript
import { TelegramAdapter } from "@andresaya/flowkit";
const telegram = new TelegramAdapter({
botToken: process.env.TELEGRAM_BOT_TOKEN!,
});
// Send a message with buttons
await telegram.send("123456789", {
text: "Welcome! What would you like to do?",
buttons: [
{ type: "postback", title: "🛒 Shop", payload: "/shop" },
{ type: "url", title: "🌐 Website", url: "https://example.com" },
],
});Telegram Webhook
typescript
app.post("/webhook/telegram", express.json(), async (req, res) => {
const message = telegram.parseMessage(req.body);
if (message) {
const result = await engine.handle(
telegram.getConversationId(message),
message.text
);
await telegram.send(message.senderId, { text: result.message });
}
res.sendStatus(200);
});SlackAdapter
Integration with Slack Apps.
typescript
import { SlackAdapter } from "@andresaya/flowkit";
const slack = new SlackAdapter({
botToken: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
});
// Send a message
await slack.send("U1234567890", {
text: "Hi! I'm your support assistant.",
buttons: [
{ type: "postback", title: "Create Ticket", payload: "ticket" },
{ type: "postback", title: "FAQ", payload: "faq" },
],
});Slack Events
typescript
app.post("/webhook/slack", express.json(), async (req, res) => {
// URL verification
if (req.body.type === "url_verification") {
return res.json({ challenge: req.body.challenge });
}
const message = slack.parseMessage(req.body);
if (message) {
const result = await engine.handle(
slack.getConversationId(message),
message.text
);
await slack.send(message.senderId, { text: result.message });
}
res.sendStatus(200);
});ResponseBuilder
Fluent API for building responses:
typescript
import { response } from "@andresaya/flowkit";
const msg = response()
.text("Choose an option:")
.button("Option A", "a")
.button("Option B", "b")
.quickReply("Help", "help")
.build();
await adapter.send(userId, msg);Channel Response Format
typescript
interface ChannelResponse {
text?: string;
quickReplies?: QuickReply[];
buttons?: Button[];
images?: string[];
cards?: Card[];
typingDelay?: number;
}
interface Button {
type: "postback" | "url" | "call";
title: string;
payload?: string;
url?: string;
phone?: string;
}
interface QuickReply {
title: string;
payload: string;
}
interface Card {
title: string;
subtitle?: string;
imageUrl?: string;
buttons?: Button[];
}Custom Channel Adapter
Create adapters for other platforms:
typescript
import type { ChannelAdapter, ChannelMessage, ChannelResponse, ChannelType } from "@andresaya/flowkit";
class DiscordAdapter implements ChannelAdapter {
channel: ChannelType = "custom";
constructor(private botToken: string) {}
parseMessage(payload: any): ChannelMessage | null {
if (!payload?.content) return null;
return {
id: payload.id,
channel: "custom",
senderId: payload.author.id,
text: payload.content,
timestamp: Date.now(),
};
}
formatResponse(response: ChannelResponse): any {
return { content: response.text };
}
async send(recipientId: string, response: ChannelResponse): Promise<void> {
const formatted = this.formatResponse(response);
// Send via Discord API
}
getConversationId(message: ChannelMessage): string {
return `discord:${message.senderId}`;
}
}Tips
- Prefix session IDs - Include channel:
whatsapp:+1234567890 - Handle rate limits - Each platform has different limits
- Test formatting - Rich content renders differently per platform
- Graceful fallback - Fall back to plain text if rich content fails