AboutBlogContact
CRM & AutomationApril 30, 2026 7 min read 7

How to Build a Telegram Mini App for Business in 2026

AunimedaAunimeda
📋 Table of Contents

How to Build a Telegram Mini App for Business in 2026

Telegram Mini Apps (TMAs) are web applications embedded inside Telegram. The user taps a button in a chat, a full-screen web app opens — no redirect to a browser, no app store download, no account creation. It runs inside Telegram, with access to the user's Telegram identity and payment method.

For businesses targeting CIS markets (Russia, Kazakhstan, Kyrgyzstan, Ukraine, Belarus), this matters enormously. Telegram penetration in these markets is 70-90%+. A Mini App reaches your customers where they already spend hours per day.


What You Can Build

The use cases that work particularly well:

E-commerce storefront. Full product catalog, cart, checkout — all inside Telegram. Telegram Payments handles the transaction. The user never leaves the app.

Appointment booking. Calendar, slot selection, confirmation — integrated with your backend. Reminder sent via the bot automatically.

Loyalty programs. Show points balance, available rewards, transaction history. Users check it frequently because it's in their daily-use app.

Food ordering for restaurants. Menu browsing, customization, payment, order status — the complete delivery flow.

Support portal. Create tickets, view status, attach files, receive updates — all in a familiar interface.


Architecture Overview

Telegram Client
      ↓
  Mini App (React/Vue/Vanilla JS — hosted on your server)
      ↓
  Telegram Bot API (validates InitData, handles payments)
      ↓
  Your Backend API (Node.js / Python / Go)
      ↓
  Database / Third-party services

The Mini App is a regular web app served over HTTPS. Telegram injects a window.Telegram.WebApp object that gives you access to the user's data, theme, and native UI components.


Step 1: Set Up the Bot

# Talk to @BotFather in Telegram
/newbot
# Choose a name and username
# Get your BOT_TOKEN

/newapp
# Link your Mini App to the bot
# Set the Web App URL (must be HTTPS)

For development, use ngrok or localtunnel to expose localhost over HTTPS.


Step 2: Frontend Setup

Install the Telegram Web App SDK:

npm create vite@latest my-mini-app -- --template react-ts
cd my-mini-app
npm install @twa-dev/sdk
npm install @twa-dev/types  # TypeScript types

Basic app structure:

// src/App.tsx
import { useEffect, useState } from 'react';
import WebApp from '@twa-dev/sdk';

interface TelegramUser {
  id: number;
  first_name: string;
  last_name?: string;
  username?: string;
  language_code?: string;
}

export default function App() {
  const [user, setUser] = useState<TelegramUser | null>(null);

  useEffect(() => {
    // Initialize the Mini App
    WebApp.ready();
    WebApp.expand(); // Full-screen mode

    // Get user data
    if (WebApp.initDataUnsafe.user) {
      setUser(WebApp.initDataUnsafe.user as TelegramUser);
    }

    // Apply Telegram theme to your app
    document.documentElement.style.setProperty(
      '--tg-theme-bg-color',
      WebApp.themeParams.bg_color ?? '#ffffff'
    );
    document.documentElement.style.setProperty(
      '--tg-theme-text-color',
      WebApp.themeParams.text_color ?? '#000000'
    );
    document.documentElement.style.setProperty(
      '--tg-theme-button-color',
      WebApp.themeParams.button_color ?? '#2481cc'
    );
  }, []);

  return (
    <div className="app">
      <h1>Hello, {user?.first_name ?? 'User'}!</h1>
    </div>
  );
}

Step 3: Validate InitData on the Backend

This is security-critical. Anyone can craft a fake initData string. You must validate it on your server before trusting user data.

// backend/src/middleware/telegram-auth.ts
import crypto from 'crypto';

export function validateTelegramInitData(initData: string, botToken: string): boolean {
  const params = new URLSearchParams(initData);
  const hash = params.get('hash');
  if (!hash) return false;

  params.delete('hash');

  // Sort parameters alphabetically
  const dataCheckString = Array.from(params.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([key, value]) => `${key}=${value}`)
    .join('\n');

  // HMAC-SHA256 with "WebAppData" as key derivation
  const secretKey = crypto
    .createHmac('sha256', 'WebAppData')
    .update(botToken)
    .digest();

  const expectedHash = crypto
    .createHmac('sha256', secretKey)
    .update(dataCheckString)
    .digest('hex');

  return hash === expectedHash;
}

export function parseTelegramUser(initData: string) {
  const params = new URLSearchParams(initData);
  const userParam = params.get('user');
  if (!userParam) return null;
  return JSON.parse(decodeURIComponent(userParam));
}

// Express middleware
export function telegramAuthMiddleware(req: any, res: any, next: any) {
  const initData = req.headers['x-telegram-init-data'] as string;

  if (!initData) {
    return res.status(401).json({ error: 'No init data' });
  }

  if (!validateTelegramInitData(initData, process.env.BOT_TOKEN!)) {
    return res.status(401).json({ error: 'Invalid init data' });
  }

  req.telegramUser = parseTelegramUser(initData);
  next();
}

On the frontend, send initData with every request:

// src/api/client.ts
import WebApp from '@twa-dev/sdk';

export async function apiFetch(path: string, options: RequestInit = {}) {
  const response = await fetch(`${import.meta.env.VITE_API_URL}${path}`, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      'X-Telegram-Init-Data': WebApp.initData,
      ...options.headers,
    },
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  return response.json();
}

Step 4: Native UI Components

The Telegram Web App SDK gives you native UI elements that look and behave like Telegram's own UI:

import WebApp from '@twa-dev/sdk';

// Main button (fixed at the bottom, Telegram-styled)
WebApp.MainButton.setText('Place Order — $24.99');
WebApp.MainButton.show();
WebApp.MainButton.onClick(() => {
  handleCheckout();
});

// Show/hide main button based on cart state
function updateMainButton(cartTotal: number) {
  if (cartTotal > 0) {
    WebApp.MainButton.setText(`Checkout — $${cartTotal.toFixed(2)}`);
    WebApp.MainButton.show();
  } else {
    WebApp.MainButton.hide();
  }
}

// Back button
WebApp.BackButton.show();
WebApp.BackButton.onClick(() => {
  navigate(-1);
  WebApp.BackButton.hide();
});

// Confirmation popup (native Telegram dialog)
WebApp.showConfirm(
  'Remove item from cart?',
  (confirmed) => {
    if (confirmed) removeFromCart(itemId);
  }
);

// Haptic feedback
WebApp.HapticFeedback.impactOccurred('medium'); // on button press
WebApp.HapticFeedback.notificationOccurred('success'); // on order success

Step 5: Telegram Payments

Telegram Payments bypass app store fees (no 30% cut). You connect a payment provider (Stripe, YooKassa, Payme, etc.) through BotFather.

// backend: create an invoice link
const bot = new TelegramBot(process.env.BOT_TOKEN!);

async function createInvoice(userId: number, order: Order) {
  const invoice = await bot.createInvoiceLink(
    `Order #${order.id}`,                    // title
    `${order.items.length} items`,           // description
    JSON.stringify({ orderId: order.id }),   // payload (returned on success)
    'USD',                                   // currency
    [{ label: 'Total', amount: order.totalCents }], // prices in cents
    {
      photo_url: 'https://yoursite.com/order-preview.jpg',
      need_name: false,
      need_email: true,
      need_phone_number: true,
    }
  );

  return invoice; // Send this URL to the Mini App
}

// frontend: open the payment dialog
const invoiceUrl = await apiFetch('/api/create-invoice', {
  method: 'POST',
  body: JSON.stringify({ orderId }),
});

WebApp.openInvoice(invoiceUrl, (status) => {
  if (status === 'paid') {
    WebApp.showAlert('Payment successful! Your order is confirmed.');
    navigate('/order-success');
  } else if (status === 'cancelled') {
    WebApp.showAlert('Payment cancelled.');
  }
});

On the backend, handle the successful payment webhook:

// backend: handle pre_checkout_query and successful_payment
bot.on('pre_checkout_query', (query) => {
  // Validate the order is still available
  bot.answerPreCheckoutQuery(query.id, true);
});

bot.on('successful_payment', async (msg) => {
  const payload = JSON.parse(msg.successful_payment!.invoice_payload);
  await markOrderPaid(payload.orderId);
  await bot.sendMessage(msg.chat.id, '✅ Order confirmed! We\'ll notify you when it ships.');
});

Step 6: Notifications Back to Users

After the Mini App session ends, you can still reach users via the bot:

async function sendOrderUpdate(telegramUserId: number, order: Order) {
  await bot.sendMessage(telegramUserId, 
    `📦 Order #${order.id} update\n\n` +
    `Status: ${order.status}\n` +
    `Estimated delivery: ${order.estimatedDelivery}`,
    {
      reply_markup: {
        inline_keyboard: [[
          {
            text: '🔍 Track Order',
            web_app: { url: `https://yourapp.com/orders/${order.id}` }
          }
        ]]
      }
    }
  );
}

Step 7: Deploy

Mini Apps require HTTPS. Production deployment options:

# nginx config for your Mini App
server {
    listen 443 ssl;
    server_name miniapp.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/miniapp.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/miniapp.yourdomain.com/privkey.pem;

    # Frontend (Vite build output)
    location / {
        root /var/www/miniapp/dist;
        try_files $uri $uri/ /index.html;
        
        # Required CORS headers for Telegram
        add_header Access-Control-Allow-Origin "https://web.telegram.org";
    }

    # Backend API
    location /api {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Testing

Telegram provides a test environment separate from production:

  1. In BotFather: /start on @BotFather → use test server bots
  2. On mobile: Settings → long press on title → Switch to Test Mode
  3. Desktop: tdesktop://testmode or launch with --test flag

For local development, set VITE_API_URL=http://localhost:3001 and use ngrok for the Mini App URL.


Launch Checklist

  • WebApp.ready() called immediately on load (stops the loading indicator)
  • WebApp.expand() called if you want full-screen
  • All API calls validate initData server-side
  • WebApp.close() called when the user completes the main action
  • Handle viewportChanged event for safe area adjustments
  • Test on both iOS and Android Telegram clients
  • Test with slow 3G network (Telegram users are global)
  • Privacy policy URL set in BotFather

Aunimeda builds Telegram Mini Apps for businesses across CIS markets — from product catalogs and booking systems to full e-commerce flows with Telegram Payments.

Contact us to scope your Mini App project. See also: Telegram Bot Development, Business Automation, Web Development

Read Also

How to Build an Instagram Bot for Business in 2026aunimeda
CRM & Automation

How to Build an Instagram Bot for Business in 2026

Instagram automation via the official Meta Graph API — DM autoresponders, comment replies, lead capture flows, and story mention responses. No grey-zone scraping, no account bans.

How to Build a WhatsApp Bot for Business in 2026: Complete Guideaunimeda
CRM & Automation

How to Build a WhatsApp Bot for Business in 2026: Complete Guide

WhatsApp has 2.7 billion active users. Here's how to build a bot that handles customer support, orders, and lead qualification automatically — without annoying your customers.

Telegram Bot vs WhatsApp Bot: Which to Build for CIS Markets (2026)aunimeda
CRM & Automation

Telegram Bot vs WhatsApp Bot: Which to Build for CIS Markets (2026)

Detailed comparison of Telegram and WhatsApp bots for Russian, Kazakh, and Kyrgyz markets. Audience data, technical capabilities, costs, and when to build each.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles