NxCreateDocs

Commands & Handlers

Register listeners for any Telegram update type using the bot global.

Commands

bot.command('start', (ctx) => {
  ctx.reply('Welcome! 👋');
});

Text matching

bot.hears('hello', (ctx) => ctx.reply('Hi!'));
bot.hears(/order #(\d+)/i, (ctx) => {
  const id = ctx.match[1];
  ctx.reply('Looking up order ' + id);
});

Callback actions

bot.action('confirm_pay', async (ctx) => {
  await ctx.answerCbQuery();
  await ctx.editMessageText('Payment confirmed ✓');
});

Fallback handlers

If you want a last-resort text response, add a general text handler after your command and pattern-specific handlers.

bot.on('text', async (ctx) => {
  await ctx.reply("Sorry, I didn't understand that. Type /help for available commands.");
});
Place broad text fallbacks near the end of your handler setup. If they come too early, more specific text handlers may never run.

Coloured buttons

Bot API 9.4 introduced a style field on InlineKeyboardButton. When the bot owner has a supported client version, buttons render with a solid background colour instead of the default transparent style.

ValueColourWhen to use
"primary"Dark blueMain action — confirm, submit, proceed
"success"GreenPositive outcome — paid, verified, done
"danger"RedDestructive action — delete, ban, cancel

Pass style directly inside the button object. Telegraf forwards unknown fields to the API as-is, so no extra setup is needed:

bot.command('pay', async (ctx) => {
  await ctx.reply('Complete your payment:', {
    reply_markup: {
      inline_keyboard: [[
        { text: '💳 Pay now',   callback_data: 'pay',    style: 'success' },
        { text: '🗑 Cancel',    callback_data: 'cancel', style: 'danger'  },
      ]],
    },
  });
});

You can also combine style with a premium custom emoji icon using icon_custom_emoji_id — requires the bot owner to have Telegram Premium:

{
  text: 'Open vault',
  callback_data: 'open_vault',
  style: 'primary',
  icon_custom_emoji_id: '4927431131898315396',
}
Older Telegram clients silently ignore style and display the button without any colour. No fallback handling is needed on your side.

Premium emojis

Telegram now allows bots to send animated premium custom emojis in messages — but only if the bot's owner has an active Telegram Premium subscription. If the owner does not have Premium, the emoji will fall back to the placeholder text inside the tag.

Use HTML parse mode and the <tg-emoji emoji-id="..."> tag, passing the custom emoji ID from Telegram's sticker set:

bot.command('greet', async (ctx) => {
  await ctx.reply(
    '<tg-emoji emoji-id="5222108309795908493">✨</tg-emoji> Welcome back, ' + ctx.from.first_name + '!',
    { parse_mode: 'HTML' }
  );
});

// Multiple premium emojis in one message
bot.command('status', async (ctx) => {
  const plan = await db.getProp(ctx.from.id, 'plan');
  await ctx.reply(
    '<b><tg-emoji emoji-id="4927431131898315396">🔒</tg-emoji> Account Status</b>\n\n' +
    '<tg-emoji emoji-id="5359441070201513074">🎭</tg-emoji> Plan: ' + (plan ?? 'Free') + '\n' +
    '<tg-emoji emoji-id="5372878055775683161">🔑</tg-emoji> Access: Active',
    { parse_mode: 'HTML' }
  );
});

You can find custom emoji IDs by forwarding a message containing the emoji to @bot_info_bot or by calling the getCustomEmojiStickers Bot API method with the emoji ID.

The bot owner's Premium status is checked server-side by Telegram. You do not need to pass any extra flag — just send the tag with a valid premium emoji ID and Telegram handles the rest.
Last updated August 11, 2026
Was this page helpful?