Telegram

Connect to Telegram with support for groups, channels, inline keyboards, and a polling fallback for local development.

Install

pnpm add @chat-adapter/telegram

Quick start

The adapter auto-detects TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET_TOKEN, and TELEGRAM_BOT_USERNAME from the environment.

lib/bot.ts
import { Chat } from "chat";
import { createTelegramAdapter } from "@chat-adapter/telegram";

const bot = new Chat({
  userName: "mybot",
  adapters: {
    telegram: createTelegramAdapter(),
  },
});

bot.onNewMention(async (thread, message) => {
  await thread.post(`You said: ${message.text}`);
});
app/api/webhooks/telegram/route.ts
import { bot } from "@/lib/bot";

export async function POST(request: Request): Promise<Response> {
  return bot.webhooks.telegram(request);
}

Configure your bot webhook in BotFather / via the Telegram API:

curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-domain.com/api/webhooks/telegram",
    "secret_token": "your-secret-token"
  }'

Configuration

Prop

Type

botToken is required — either via config or env var.

Authentication

Create a bot via BotFather:

  1. Send /newbot and follow the prompts.
  2. Copy the token to TELEGRAM_BOT_TOKEN.
  3. Optionally pick a username and copy it to TELEGRAM_BOT_USERNAME.

Advanced

Polling for local development

lib/bot.ts
import { Chat } from "chat";
import { createTelegramAdapter } from "@chat-adapter/telegram";
import { createMemoryState } from "@chat-adapter/state-memory";

const telegram = createTelegramAdapter({
  mode: "polling",
});

const bot = new Chat({
  userName: "mybot",
  adapters: { telegram },
  state: createMemoryState(),
});

Polling and webhooks are mutually exclusive in Telegram. mode: "polling" deletes the webhook by default before calling getUpdates.

Auto mode

mode: "auto" (the default) checks getWebhookInfo: if a webhook URL is set, it uses webhook mode; otherwise it falls back to polling on long-running runtimes. If getWebhookInfo fails, the adapter stays in webhook mode (safe fallback).

const telegram = createTelegramAdapter({ mode: "auto" });
void bot.initialize();
console.log(telegram.runtimeMode); // "webhook" | "polling"

Markdown formatting

Outbound messages are sent with Telegram's MarkdownV2 parse mode. The adapter walks the markdown AST and emits MarkdownV2 with context-aware escaping (text vs. code blocks vs. link URLs), so you author standard markdown and the adapter handles every reserved character.

Pass { raw: "..." } only if you need to ship a fully pre-escaped MarkdownV2 string.

Notes

  • Telegram does not expose full historical message APIs to bots. fetchMessages returns adapter-cached messages from the current process.
  • listThreads is not available for Telegram chats.
  • Telegram callback data is limited to 64 bytes — keep Button id/value payloads short.
  • Other rich card elements (images, select menus, radios) render as fallback text.

Feature support

Messaging

FeatureSupported
Post message
Edit message
Delete message
File uploadsSingle file
StreamingPost+Edit
Scheduled messages

Rich content

FeatureSupported
Card formatMarkdownV2 + inline keyboard
ButtonsInline keyboard
Link buttonsInline keyboard URLs
Select menus
TablesASCII
Fields
Images in cards
Modals

Conversations

FeatureSupported
Slash commands
Mentions
Add reactions
Remove reactions
Typing indicator
DMs
Ephemeral messages
User lookupSeen users
Parent subject
Native client
Custom API endpoint

Message history

FeatureSupported
Fetch messagesCached
Fetch single messageCached
Fetch thread info
Fetch channel messagesCached
List threads
Fetch channel info
Post channel message

On this page