Looking for the chatbot template? It's now here.
Vercel
GitHub

@zernio/chat-sdk-adapter

Official Zernio adapter for Chat SDK. Build chatbots that work across Instagram, Facebook, Twitter/X, Telegram, WhatsApp, Bluesky, and Reddit through a single integration.

Even with native Chat SDK adapters for each platform, you'd still need to apply to Meta's developer program, go through App Review, get WhatsApp Business verification, apply for X elevated access, and more. With Zernio, your users connect accounts in a dashboard and you get one API key. No developer programs, no app reviews, no token management.

Installation

bash
npm install @zernio/chat-sdk-adapter chat

Quick Start

typescript
import { Chat } from "chat";import { createZernioAdapter } from "@zernio/chat-sdk-adapter";const bot = new Chat({  adapters: {    zernio: createZernioAdapter(),  },  onNewMessage: async ({ thread, message }) => {    // This handler fires for messages from ALL connected platforms    const platform = message.raw.platform; // "instagram", "telegram", etc.    await thread.post(`Hello from ${platform}!`);  },});

Next.js Webhook Route

typescript
// app/api/chat-webhook/route.tsimport { bot } from "@/lib/bot";export async function POST(request: Request) {  return bot.webhooks.zernio(request);}

Configuration

Environment Variables

VariableRequiredDescription
ZERNIO_API_KEYYesYour Zernio API key for sending messages
ZERNIO_WEBHOOK_SECRETRecommendedHMAC-SHA256 secret for verifying inbound webhooks
ZERNIO_API_BASE_URLNoOverride API base URL (default: https://zernio.com/api)
ZERNIO_BOT_NAMENoBot display name (default: "Zernio Bot")

Explicit Configuration

typescript
const adapter = createZernioAdapter({  apiKey: "your-api-key",  webhookSecret: "your-webhook-secret",  baseUrl: "https://zernio.com/api",  botName: "My Bot",});

Setup

1. Get a Zernio API Key

Sign up at zernio.com and create an API key from the dashboard. Make sure the key has read-write permissions.

2. Connect Social Accounts

Connect the social accounts you want your bot to handle through the Zernio dashboard or API.

3. Configure a Webhook

Create a webhook in your Zernio dashboard pointing to your bot's webhook endpoint:

  • URL: https://your-app.com/api/chat-webhook
  • Events: Select message.received and comment.received
  • Secret: Set a strong secret and pass it as ZERNIO_WEBHOOK_SECRET

4. Enable the Inbox Addon

The inbox addon must be enabled on your Zernio account to receive message webhooks.

How It Works

Incoming message flow:  User sends DM on Instagram/Telegram/etc.    -> Platform delivers to Zernio    -> Zernio fires message.received webhook    -> Adapter verifies signature & parses payload    -> chat-sdk processes message through your handlersOutgoing message flow:  Your handler calls thread.post("Hello!")    -> Adapter calls Zernio REST API    -> Zernio delivers to the correct platform    -> User receives the message on Instagram/Telegram/etc.

Thread ID Format

Thread IDs follow the format zernio:{accountId}:{conversationId}:

  • accountId: The Zernio social account ID (which platform account received the message)
  • conversationId: The Zernio conversation ID (the specific DM thread)
  • For comments: zernio:{accountId}:comment:{postId}
typescript
import { ZernioAdapter } from "@zernio/chat-sdk-adapter";// Decode a thread ID to get platform-specific detailsconst adapter = new ZernioAdapter({ apiKey: "..." });const { accountId, conversationId } = adapter.decodeThreadId(threadId);

Supported Features

FeatureSupportedNotes
Send messagesYesText messages across all platforms
Rich messages (cards)YesButtons and templates on FB, IG, Telegram, WhatsApp
Edit messagesPartialTelegram only
Delete messagesPartialTelegram, X (full delete); Bluesky, Reddit (self-only)
ReactionsPartialTelegram and WhatsApp (add/remove emoji)
Typing indicatorsPartialFacebook Messenger and Telegram
AI streamingPartialPost+edit on Telegram; single post on others
File attachmentsYesVia media upload endpoint
Fetch messagesYesFull conversation history
Fetch thread infoYesParticipant details, platform, status
Webhook verificationYesHMAC-SHA256 signature
Comment webhooksYescomment.received routed through handlers

Platform Support Matrix

FeatureFBIGTelegramWhatsAppXBlueskyReddit
Send textYYYYYYY
ButtonsYYYY---
TypingY-Y----
Delete--Y-YSelfSelf
Reactions--YY---
MediaYYYYY--
Edit--Y----

Rich Messages

The adapter maps chat-sdk Card elements to native platform formats instead of rendering as fallback text:

typescript
import { Card, Button, Actions, Text } from "chat";await thread.post(  Card({    title: "Order #1234",    subtitle: "Total: $50.00",    imageUrl: "https://example.com/product.jpg",    children: [      Text("Your order is ready for pickup."),      Actions([        Button({ id: "confirm", label: "Confirm", style: "primary" }),        LinkButton({ label: "Track Order", url: "https://example.com/track" }),      ]),    ],  }));// Renders as interactive card on FB/IG/Telegram/WhatsApp// Falls back to text on X/Bluesky/Reddit

AI Streaming

Stream AI responses with the post+edit pattern (works best on Telegram):

typescript
import { generateText } from "ai";import { openai } from "@ai-sdk/openai";onNewMessage: async ({ thread, message }) => {  const result = await generateText({    model: openai("gpt-4o"),    prompt: message.text,  });  // On Telegram: posts initial message, edits as tokens arrive  // On other platforms: collects full response, posts once  await thread.stream(result.textStream);}

Platform-Specific Data

Access the underlying platform through the raw message:

typescript
onNewMessage: async ({ message }) => {  const raw = message.raw;  // Check which platform the message came from  console.log(raw.platform); // "instagram", "facebook", "telegram", etc.  // Access platform-specific sender info  if (raw.sender.instagramProfile) {    console.log(`Follower count: ${raw.sender.instagramProfile.followerCount}`);    console.log(`Is verified: ${raw.sender.instagramProfile.isVerified}`);  }  // WhatsApp phone number  if (raw.sender.phoneNumber) {    console.log(`Phone: ${raw.sender.phoneNumber}`);  }}

API Client

The adapter exports a standalone API client for direct Zernio API calls:

typescript
import { ZernioApiClient } from "@zernio/chat-sdk-adapter";const client = new ZernioApiClient("your-api-key", "https://zernio.com/api");// List conversationsconst { data, pagination } = await client.listConversations({  platform: "instagram",  status: "active",  limit: 20,});// Fetch messagesconst messages = await client.fetchMessages(conversationId, accountId);// Send typing indicatorawait client.sendTyping(conversationId, accountId);// Add reactionawait client.addReaction(conversationId, messageId, accountId, "👍");// Upload mediaconst { url } = await client.uploadMedia(fileBuffer, "image/jpeg");

Webhook Verification

The adapter automatically verifies webhook signatures when webhookSecret is configured. You can also use the verification utility directly:

typescript
import { verifyWebhookSignature } from "@zernio/chat-sdk-adapter";const isValid = verifyWebhookSignature(rawBody, signature, secret);

Error Handling

The adapter maps Zernio API errors to standard chat-sdk error classes:

HTTP StatusError ClassDescription
401AuthenticationErrorInvalid or expired API key
403PermissionErrorRead-only key, missing addon, etc.
404ResourceNotFoundErrorConversation or message not found
429AdapterRateLimitErrorRate limit hit (includes retryAfter)
5xxNetworkErrorServer error

License

MIT