
Introduction: Why Telegram Notifications?
Telegram bots are one of the easiest ways to add real-time notifications to your web application. Unlike email (which can end up in spam) or SMS (which costs money), Telegram notifications are instant, free, and reliable. They're perfect for:
- Admin alerts when important actions happen
- System status updates
- User activity notifications
- Error alerts and monitoring
The best part? You can set it up in about 10 minutes, and it works from any server-side code - Node.js, Python, PHP, you name it.
Step 1: Create Your Bot
First, you need to create a bot and get your authentication token.
- Open Telegram and search for @BotFather (the official bot for creating bots)
- Send the command
/newbot - Follow the prompts to name your bot (e.g., "My App Notifications")
- Choose a unique username ending in
bot(e.g.,myapp_notifications_bot) - @BotFather will give you an HTTP API token - save this immediately!
The token looks like: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz
⚠️ Important: Keep this token secret. Anyone with it can control your bot.
Step 2: Configure Bot Privacy
If you want your bot to work in groups (which is common for notifications), you need to disable privacy mode:
- Go to @BotFather →
/mybots - Select your bot
- Choose Bot Settings → Allow Groups? → Allow
Now add your bot to the group where you want to receive notifications. For full functionality, you can make it an administrator, but for basic notifications, a regular member is fine.
Step 3: Get Your Chat ID
Your web application needs to know which chat (group or user) to send messages to. The chat ID is a unique identifier.
Method 1: Using Telegram Web (Easiest)
- Visit web.telegram.org in your browser
- Open the group where you added your bot
- Look at the URL in your browser's address bar
- The chat ID is in the URL - it will be a negative number like
-1001234567890

The chat ID is visible in the URL when viewing a group on Telegram Web
Method 2: Using the Bot API
- Make sure your bot is in the group
- Send a message in the group (mentioning the bot is optional)
- In your browser or using curl, visit:
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates - Look for the
"chat"object in the JSON response - Copy the
"id"value (it will be negative for groups)
Note: For groups, the chat ID always starts with a minus sign. For direct messages to users, it's a positive number.
Step 4: Basic Implementation
Now let's write the code. Here's a minimal, clean implementation:
// lib/telegram.ts
"use server";
interface TelegramNotification {
message: string;
chatId?: string; // Optional, defaults to env var
}
export async function sendTelegramNotification({
message,
chatId,
}: TelegramNotification) {
const botToken = process.env.TELEGRAM_BOT_TOKEN;
const defaultChatId = process.env.TELEGRAM_CHAT_ID;
// Use provided chatId or fall back to env var
const targetChatId = chatId || defaultChatId;
if (!botToken || !targetChatId) {
console.warn("Telegram not configured");
return { success: false, reason: "not_configured" };
}
const telegramUrl = `https://api.telegram.org/bot${botToken}/sendMessage`;
try {
const response = await fetch(telegramUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: targetChatId,
text: message,
}),
});
const data = await response.json();
if (!data.ok) {
console.error("Telegram API error:", data);
return { success: false, error: data.description };
}
return { success: true, messageId: data.result.message_id };
} catch (error) {
console.error("Error sending Telegram message:", error);
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}That's it! This is a minimal, production-ready implementation. Let's break it down:
- Server-only: The
"use server"directive ensures this only runs on the server - Environment variables: Uses
TELEGRAM_BOT_TOKENandTELEGRAM_CHAT_IDfrom your.envfile - Error handling: Gracefully handles missing config or API errors
- Simple API: Just pass a message string
Step 5: Set Up Environment Variables
Add these to your .env.local file:
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
TELEGRAM_CHAT_ID=-1001234567890Important: Make sure the chat ID includes the minus sign for groups!
Step 6: Use It in Your Application
Now you can call it from anywhere in your server-side code:
// Example: Send notification when user signs up
import { sendTelegramNotification } from "@/lib/telegram";
export async function createUser(userData: UserData) {
// ... create user logic ...
// Send notification (non-blocking)
sendTelegramNotification({
message: `New user signed up: ${userData.email}`,
}).catch((error) => {
// Don't break user creation if notification fails
console.error("Failed to send notification:", error);
});
return { success: true };
}Advanced: Formatted Messages
Telegram supports Markdown formatting. Here's an enhanced version:
export async function sendFormattedNotification({
title,
message,
metadata,
}: {
title: string;
message: string;
metadata?: Record<string, string>;
}) {
const botToken = process.env.TELEGRAM_BOT_TOKEN;
const chatId = process.env.TELEGRAM_CHAT_ID;
if (!botToken || !chatId) {
return { success: false, reason: "not_configured" };
}
// Build formatted message
let formattedMessage = `*${title}*\n\n${message}`;
if (metadata) {
formattedMessage += "\n\n";
Object.entries(metadata).forEach(([key, value]) => {
formattedMessage += `• ${key}: ${value}\n`;
});
}
const telegramUrl = `https://api.telegram.org/bot${botToken}/sendMessage`;
const response = await fetch(telegramUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: chatId,
text: formattedMessage,
parse_mode: "MarkdownV2", // Enable Markdown formatting
}),
});
const data = await response.json();
return data.ok
? { success: true }
: { success: false, error: data.description };
}
Note: When using MarkdownV2, you need to escape special characters. For simplicity, you can use parse_mode: "HTML" instead, which is more forgiving.
Common Issues and Solutions
"Bad Request: chat not found"
This usually means:
- The bot isn't in the group
- The chat ID is wrong (missing the minus sign for groups)
- The chat ID is for a different chat
Solution: Double-check your chat ID and make sure the bot is in the group.
"Unauthorized"
This means your bot token is invalid or expired.
Solution: Get a new token from @BotFather using /token.
Messages Not Appearing
If your code runs but you don't see messages:
- Check the API response - look for error messages
- Verify the bot is still in the group (it might have been removed)
- Check if the group has restrictions on bot messages
- Make sure you're checking the right chat/group
Rate Limiting
Telegram allows about 30 messages per second per bot. If you're sending many notifications:
// Add a small delay between messages
for (const notification of notifications) {
await sendTelegramNotification(notification);
await new Promise((resolve) => setTimeout(resolve, 50)); // 50ms delay
}Quick Checklist
Before you finish, make sure:
- ✅ Bot created and token saved
- ✅ Bot added to your notification group
- ✅ Chat ID obtained (with minus sign for groups)
- ✅ Environment variables set in
.env.local - ✅ Test notification sent successfully
- ✅ Error handling in place
- ✅ Bot token not committed to git
Real-World Example: Audit Logging
Here's how you might use it for audit logging:
// lib/audit.ts
import { sendTelegramNotification } from "@/lib/telegram";
export async function logActivity(
action: string,
actor: string,
details?: string
) {
// Log to database
await saveToDatabase({ action, actor, details });
// Send Telegram notification
const message = `🔔 *${action}*\n\n👤 Actor: ${actor}${
details ? `\n📝 ${details}` : ""
}`;
sendTelegramNotification({ message }).catch((error) => {
// Don't break logging if Telegram fails
console.error("Notification failed:", error);
});
}Now every important action in your app can trigger a Telegram notification automatically.