Telegram Bot Development with Node.js and grammY: Lead Collection, Sessions, Webhook, and 429 Error Protection

Разработка Telegram-ботов on Node.js has long gone beyond simple auto-responder scripts. Modern business solutions require reliable transaction processing, high load tolerance, secure webhook handling, and flexible conversation management (FSM). The previously popular node-telegram-bot-api library is now outdated. Today, the grammY framework is becoming the industry standard for JavaScript/TypeScript developers.

In this guide, we will create a production-ready Telegram bot on Node.js. We will implement secure handling of referral links (Deep Linking), build a linear conversation to collect client contacts with unique application ID generation, set up an Express server for Webhooks with secret token validation, and look at how to avoid crashes when hitting HTTP 429 rate limits.

Project Initialization and Secure Deep Linking Handling

Let's start with project initialization and basic setup. To get started, we will need the grammy and dotenv packages to securely store the bot token in environment variables.

When a user launches a bot for the first time, it is often necessary to pass context—for example, a partner ID or a promo code. The Deep Linking mechanism is used for this: links in the format t.me/YourBot?start=payload. When following such a link, Telegram sends the /start command with a parameter in the text field. Our task is to securely extract and validate this parameter.

import { Bot } from "grammy";
import dotenv from "dotenv";

dotenv.config();

const token = process.env.BOT_TOKEN;
if (!token) {
throw new Error("BOT_TOKEN не задан в переменных окружения!");
}

// Инициализация бота
const bot = new Bot(token);

// Обработка команды /start с поддержкой deep-linking
bot.command("start", async (ctx) => {
const payload = ctx.match; // Содержит строку после ?start=

if (payload) {
// Валидируем входящие данные: разрешаем только латиницу, цифры и дефис
const cleanPayload = payload.replace(/[^a-zA-Z0-9_-]/g, "");

if (cleanPayload.length > 64) {
return ctx.reply("Ошибка: Некорректный реферальный код.");
}

await ctx.reply(`Добро пожаловать! Вы перешли по партнерской ссылке: ${cleanPayload}`);
} else {
await ctx.reply("Приветствуем! Нажмите /register, чтобы оставить заявку на обслуживание.");
}
});

// Запуск в режиме Long Polling (для локальной разработки)
if (process.env.NODE_ENV !== "production") {
bot.start();
console.log("Бот запущен локально через Long Polling");
}

Step-by-Step Lead Collection with the Conversations Plugin

To implement step-by-step scenarios (for example, surveys or booking services), grammY uses the powerful @grammyjs/conversations plugin. Unlike classic finite state machines (FSM), where you have to manually save the user's step to a database and write dozens of branches, this plugin allows you to write sequential asynchronous code using await conversation.waitFor().

Before starting a conversation, let's set up the built-in session storage. In real production, it is recommended to replace the default in-memory storage with a Redis adapter so that sessions are not reset when the Node.js process restarts.

Also, keep in mind an important Telegram API limitation: the size of the callback_data parameter in inline buttons must not exceed 64 bytes. You cannot pass complex data structures there—generate short hashes and link them to entities in the database.

import { session } from "grammy";
import { conversations, createConversation } from "@grammyjs/conversations";
import crypto from "crypto";

// Настройка middleware сессий
bot.use(session({ initial: () => ({}) }));
bot.use(conversations());

/**
* Сценарий сбора контактных данных лида
*/
async function collectLeadConversation(conversation, ctx) {
await ctx.reply("Пожалуйста, введите ваше имя и фамилию:");

// Ожидаем текстовое сообщение от пользователя
const nameCtx = await conversation.waitFor("message:text");
const clientName = nameCtx.message.text.trim();

await ctx.reply("Отлично! Теперь отправьте ваш номер телефона для связи:");

const phoneCtx = await conversation.waitFor("message:text");
const clientPhone = phoneCtx.message.text.trim();

// Генерация криптографически стойкого ID лида
const leadId = crypto.randomBytes(7).toString("hex");

// Сохраняем лид во внешнюю БД (имитация через conversation.external)
await conversation.external(async () => {
// В реальном проекте здесь будет: await db.leads.insert({ id: leadId, name: clientName, phone: clientPhone })
console.log(`[DB] Создан лид #${leadId}: ${clientName} (${clientPhone})`);
});

await ctx.reply(`Спасибо! Ваша заявка #${leadId} успешно зарегистрирована. Наш менеджер свяжется с вами.`);
}

// Регистрация диалога во фреймворке
bot.use(createConversation(collectLeadConversation));

// Команда для запуска диалога
bot.command("register", async (ctx) => {
await ctx.conversation.enter("collectLeadConversation");
});

Production Deployment: Webhooks with Secret Token Validation

For local development, Long Polling (the getUpdates method) is great, but in production under high loads, using Webhooks is an absolute standard. Webhooks reduce bot response latency and save server resources because Telegram itself sends events (Updates) to your HTTPS server.

To protect your endpoint from unauthorized requests, Telegram allows you to set a secret token when registering a webhook (the secret_token parameter in the setWebhook method). Telegram will send this token in the X-Telegram-Bot-API-Secret-Token HTTP header with every request. Our Node.js server must validate this header.

Below is an example of integrating the bot with an Express web server:

import express from "express";
import { webhookCallback } from "grammy";

if (process.env.NODE_ENV === "production") {
const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3000;
const SECRET_TOKEN = process.env.TELEGRAM_SECRET_TOKEN;

if (!SECRET_TOKEN) {
throw new Error("Критическая ошибка: TELEGRAM_SECRET_TOKEN не задан!");
}

// Middleware для проверки подлинности запросов от Telegram
const verifyTelegramSecret = (req, res, next) => {
const receivedToken = req.headers["x-telegram-bot-api-secret-token"];
if (receivedToken !== SECRET_TOKEN) {
console.warn("Попытка несанкционированного доступа к вебхуку!");
return res.status(403).send("Forbidden");
}
next();
};

// Роут вебхука
app.post(
"/telegram-webhook",
verifyTelegramSecret,
webhookCallback(bot, "express")
);

app.listen(PORT, () => {
console.log(`Сервер вебхуков запущен на порту ${PORT}`);
});
}

Rate Limit Trap: Handling HTTP 429 (Too Many Requests) Errors

When scaling your bot, you will inevitably run into strict Telegram Bot API limits:

  • No more than 30 messages per second in total to all chats;
  • No more than 1 message per second to a specific private chat;
  • No more than 20 messages per minute to a single group/supergroup.

If your application exceeds these limits (for example, during bulk notifications about order status or promotions), Telegram returns an HTTP 429 Too Many Requests error with a retry_after parameter in the response body. If you ignore this status and continue spamming the API, Telegram will temporarily block your bot's token.

To bypass this issue, the grammY ecosystem offers an official request auto-retry plugin—@grammyjs/auto-retry. It intercepts 429 errors, pauses requests for the time specified by the server, and automatically retries them.

import { autoRetry } from "@grammyjs/auto-retry";

// Подключаем плагин автоповтора к API-клиенту бота
bot.api.config.use(
autoRetry({
maxDelaySeconds: 30, // Максимальное время ожидания перед повторной попыткой
maxRetryAttempts: 3, // Количество попыток отправить запрос повторно
})
);

Using the plugin ensures that your users are guaranteed to receive their notifications, and the Node.js process won't crash due to an unhandled promise rejection.

Conclusion

We have created a reliable skeleton for a Telegram bot on Node.js using the grammY library. We learned how to securely capture leads through linear conversations, protected our Webhook endpoint with a secret token, and implemented a mechanism to automatically bypass API rate limits. This architecture is ready for integration with CRM systems and databases in real-world businesses.

If you need the development of a professional and fault-tolerant turnkey Telegram bot, contact the specialists at BotCreator.

New articles on Telegram

We explain what to automate in your business and how it works in practice. No spam.