Skip to main content
POST
/
api
/
telegram
/
webhook
Telegram Webhook
curl --request POST \
  --url https://api.example.com/api/telegram/webhook \
  --header 'Content-Type: <content-type>' \
  --data '
{
  "update_id": 123,
  "message": {
    "message.message_id": 123,
    "message.from": {
      "message.from.id": 123,
      "message.from.username": "<string>",
      "message.from.first_name": "<string>",
      "message.from.last_name": "<string>"
    },
    "message.text": "<string>",
    "message.chat": {}
  },
  "callback_query": {
    "callback_query.id": "<string>",
    "callback_query.from": {},
    "callback_query.data": "<string>"
  }
}
'
{
  "verify_basic": "<string>",
  "verify_biweekly": "<string>",
  "verify_monthly": "<string>",
  "verify_premium": "<string>",
  "verify_promo": "<string>",
  "settings_*": "<string>"
}

Overview

This webhook endpoint receives updates from the Telegram Bot API and handles all bot commands, messages, and callback queries. It processes subscription payments, manages MT5 copier setup, and controls access to the VIP channel.

Endpoint

POST /api/telegram/webhook

Request Headers

Content-Type
string
required
Must be application/json

Request Body

The request body follows the Telegram Bot API Update object structure:
update_id
number
required
The update’s unique identifier
message
object
New incoming message of any kind
callback_query
object
New incoming callback query from inline keyboard buttons

Supported Commands

User Commands

/start

Admin Commands

/broadcast Your message here

Command Behaviors

/start

Displays welcome message with plan details and pricing information.

/pay

Initiates payment flow:
  1. Prompts user for email address
  2. Validates email format using regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  3. Generates payment links for all plans via /api/payment/link
  4. Returns inline keyboard with payment buttons

/verify_* Commands

Verifies payment and grants access:
Rate Limiting: 3 failed attempts per user, then 1-hour block (configured in RATE_LIMIT.maxAttempts and RATE_LIMIT.blockDurationMs)
Verification Flow:
  1. Check if reference is already being processed (idempotency)
  2. Validate reference format (non-empty string)
  3. Check for promo codes (EXTRA, EXTRA2, VIP, DISCOUNT)
  4. Check custom promo codes in database
  5. Verify transaction with Paystack API
  6. Validate payment amount matches plan
  7. Create subscription record
  8. Generate and send invite link
  9. For Premium plans: Auto-start MT5 setup flow
Promo Codes:
  • EXTRA - 1 week Premium + Meta Copier (FREE)
  • EXTRA2 - 2 weeks Premium + Meta Copier (FREE)
  • VIP - 1 week Basic (FREE)
  • DISCOUNT - 1 week Basic (₦3,000) - generates payment link

/status

Returns current subscription status including:
  • Plan type and name
  • Start and expiry dates
  • Days remaining
  • Copier access status

/mt5setup

Initiates MT5 copier setup flow (Premium only): Requirements:
  • Active Premium subscription with hasCopierAccess: true
  • Cent account (not Standard)
  • Broker: Headway
  • Server: headway-real
  • Magic Number: 123456
Setup Flow:
  1. Collect MT5 account number
  2. Collect MT5 password (encrypted before storage)
  3. Verify credentials with MetaCopier API
  4. Create copier settings with defaults

/settings

Displays copier configuration menu with inline keyboard (Premium only):
  • Lot Size multiplier
  • Max Lot Size
  • Maximum Lot Total
  • Max Open Positions
  • Copy Stop Loss (on/off)
  • Copy Take Profit (on/off)
  • TP2 Enabled (on/off)

Callback Query Handlers

Inline keyboard buttons trigger callback queries with these data values:
verify_basic
string
Prompts user to enter Basic plan payment reference
verify_biweekly
string
Prompts user to enter Bi-Weekly plan payment reference
verify_monthly
string
Prompts user to enter Monthly plan payment reference
verify_premium
string
Prompts user to enter Premium plan payment reference
verify_promo
string
Prompts user to enter Promo payment reference
settings_*
string
Settings menu callbacks for copier configuration

Conversation State Management

The bot maintains conversation states for multi-step flows:

Email Collection State

  • Triggered by /pay command
  • Validates email format
  • Generates payment links after email submission

MT5 Setup State Machine

States:
  1. account_number - Waiting for MT5 account number
  2. password - Waiting for MT5 password
  3. confirm_setup - Waiting for setup confirmation

Promo Payment State

  • Tracks users waiting to verify promo payments
  • Links expire after 48 hours (configured in PROMO_EXPIRY_HOURS)

Rate Limiting

Failed Verification Attempts: Users are blocked after 3 failed payment verification attempts. Block duration: 1 hour.
Rate limit implementation:
  • In-memory store: Map<userId, { count: number; blockedUntil: number }>
  • Max attempts: 3 (from RATE_LIMIT.maxAttempts)
  • Block duration: 3600000ms (1 hour)
  • Reset on successful verification

Idempotency

The webhook uses an in-memory Set to track references currently being processed, preventing duplicate subscription creation from concurrent requests.
const processingReferences = new Set<string>()

Response

The endpoint always returns 200 OK to acknowledge receipt of the update:
{
  "ok": true
}
Telegram requires webhooks to respond within 60 seconds. This endpoint processes updates asynchronously and responds immediately.

Error Handling

Errors are communicated to users via Telegram messages, not HTTP responses:

User Errors

❌ Too many failed verification attempts. Please try again in 1 hour.

System Errors

❌ Database Error

Failed to save your subscription. Please contact support.

Database Operations

The webhook performs these database operations:

Subscription Creation

await prisma.subscription.create({
  data: {
    telegramUserId: string,
    telegramUsername: string,
    telegramName: string,
    paystackRef: string,
    customerEmail: string,
    amountKobo: number,
    planType: PlanType,
    hasCopierAccess: boolean,
    startedAt: Date,
    expiresAt: Date,
    inviteLinkUsed: string
  }
})

MT5 Setup Creation

await prisma.mt5Setup.create({
  data: {
    subscriptionId: string,
    accountNumber: string,
    accountPassword: string, // encrypted
    copierMultiplier: number,
    lotSize: number,
    maxLotSize: number,
    maximumLot: number,
    maxOpenPositions: number,
    copyStopLoss: boolean,
    copyTakeProfit: boolean,
    tp2Enabled: boolean,
    setupStatus: 'active' | 'inactive'
  }
})

Security Considerations

  1. Password Encryption: MT5 passwords are encrypted using AES-256 before storage
  2. Rate Limiting: Prevents brute force verification attempts
  3. Idempotency: Prevents duplicate processing of same reference
  4. Admin Verification: Admin commands check user.id === ADMIN_ID
  5. Reference Validation: Checks for existing subscriptions with same reference

External API Calls

Telegram Bot API

await sendMessage(userId, message)

Paystack API

await verifyTransaction(reference)

MetaCopier API

await createMt5Account(accountNumber, password)
await updateCopierSettings(accountNumber, settings)
await removeUserMt5Account(accountNumber)

Example Webhook Payloads

{
  "update_id": 123456789,
  "message": {
    "message_id": 1234,
    "from": {
      "id": 987654321,
      "is_bot": false,
      "first_name": "John",
      "username": "johndoe"
    },
    "chat": {
      "id": 987654321,
      "first_name": "John",
      "username": "johndoe",
      "type": "private"
    },
    "date": 1234567890,
    "text": "/start"
  }
}

Build docs developers (and LLMs) love