Triggers
Every flow declares exactly one trigger after on. This is the precise reference; for a gentle overview see Core Concepts β Triggers.
on commandβ
flow start on command "/start" { /* β¦ */ }
Fires when a user sends the slash command. Command context is available via command.*, and message.* for the underlying message.
on message [filter]β
flow on_text on message [text] { /* β¦ */ }
flow on_photo on message [photo] { /* β¦ */ }
flow anything on message [any] { /* β¦ */ }
Fires on incoming messages matching the filter. The message is available as message.* (e.g. message.text, message.message_id, message.reply_to_message).
Filters: photo, video, audio, voice, document, text, sticker, animation, contact, location, poll, dice, any.
on callback "data"β
flow menu on callback "menu" { /* β¦ */ }
flow buy on callback "buy:" { /* prefix match */ }
Fires when an inline keyboard button is tapped. The string matches the button's callback_data. A trigger ending in : matches any payload starting with that prefix; exact matches take priority, and among prefixes the longest match wins. The full payload is callback.data; also available: callback.message, callback.from, callback.id.
on schedule "cron"β
flow daily on schedule "0 9 * * *" { /* every day 09:00 */ }
flow monday on schedule "0 9 * * 1" { /* Mondays 09:00 */ }
Fires on a standard 5-field cron expression (min hour day month weekday). Scheduled flows have no chat in scope β send to an explicit chat_id.
on webhook(...)β
flow ping on webhook "health" { respond.text("ok") }
flow ipn on webhook("nowpayments",
verify: hmac, sig_algo: sha512, sig_payload: sorted_json,
header: "x-nowpayments-sig", secret: global.nowpayments_ipn_secret, methods: [post]
) with body: { payment_id: Number, payment_status: String } {
respond.json({ ok: true })
}
Fires on an inbound HTTP request to the bot's webhook URL. Full options (verification modes, typed bodies, request.*, respond.*) are in Webhooks.
Group eventsβ
| Trigger | Fires when⦠| Key context |
|---|---|---|
on join | A user joins the chat | member.new_chat_member.user.* |
on leave | A user leaves or is removed | member.new_chat_member.user.* |
on join_request | A user requests to join | request.from.*, request.invite_link |
on bot_added | The bot is added to a chat | β |
on bot_removed | The bot is removed from a chat | β |
on member [filter] | A membership sub-event occurs | member.new_chat_member.user.* |
flow greet on join {
set { flow.name = member.new_chat_member.user.first_name }
send.text(`π Welcome, ${flow.name}!`)
}
flow review on join_request {
set { flow.uid = request.from.id }
chat.approve(user_id: flow.uid)
}
flow on_ban on member [banned] {
set { flow.name = member.new_chat_member.user.first_name }
send.text(`π¨ ${flow.name} was banned.`)
}
member filters: joined, left, banned, unbanned, promoted, demoted, restricted.
Payments (Telegram Stars)β
A Stars purchase fires two triggers in sequence: on pre_checkout (validate, ~10s window) then on successful_payment (deliver the goods).
on pre_checkoutβ
flow precheck on pre_checkout {
set { flow.prod = array.find(shared.products, "key", pre_checkout.invoice_payload) }
when types.isNil(flow.prod) {
debug.log(`pre_checkout REJECT: unknown '${pre_checkout.invoice_payload}'`)
}
}
Fires on a pre_checkout_query before the charge clears. The runtime auto-approves so the charge proceeds β use this flow to log mismatches or persist intent.
Scope: pre_checkout.* β invoice_payload, total_amount (star count), currency, id.
on successful_paymentβ
flow paid on successful_payment {
set { user.owned << [{
key: payment.invoice_payload,
price: payment.total_amount,
charge_id: payment.telegram_payment_charge_id,
at: time.timestamp()
}] }
send.text(`β
Payment received β β${payment.total_amount}. Enjoy!`)
}
Fires when a payment completes. Deliver the product here.
Scope: payment.* β total_amount, invoice_payload, currency, telegram_payment_charge_id, provider_payment_charge_id.
See Messaging β send.invoice to start a Stars invoice. Reference blueprint: stars-shop.botgami.
Telegram Businessβ
For bots connected to a Telegram Business account. Inside these flows, send.* automatically replies as the connected account owner.
| Trigger | Fires when⦠| Key scope |
|---|---|---|
on business_connection | The account connects or disconnects the bot | business.connection_id, business.can_reply, business.is_enabled, business.user |
on business_message | A customer DMs the connected account | business.text, business.connection_id, business.from |
on edited_business_message | A message in a connected chat is edited | business.* (new content) |
on deleted_business_messages | Messages in a connected chat are deleted | business.connection_id |
flow connected on business_connection {
when business.is_enabled {
send.text(`β
Connected. Reply rights: ${business.can_reply}`)
} else {
send.text("βΉοΈ Connection disabled.")
}
}
flow concierge on business_message {
// This reply is sent AS the account owner automatically.
send.text(`Thanks for your message: ${business.text}`)
}
Reference blueprint: business-concierge.botgami.
Mini Appsβ
on web_app_dataβ
Fires when a Telegram Mini App sends data back to the bot (WebApp.sendData). Always authenticate before trusting the payload.
flow on_webapp on web_app_data {
set { flow.payload = json.parse(web_app.data) }
when !crypto.verifyInitData(util.coalesce(flow.payload.initData, ""), global.bot_token) {
send.text("β Couldn't verify that request.")
stop
}
send.text(`Action: ${flow.payload.action}`)
}
Scope: web_app.* β data (raw payload string), button_text. Authenticate with crypto.verifyInitData(web_app.data, global.bot_token).
Reference blueprint: miniapp-rewards.botgami.
Reactionsβ
on reactionβ
Fires when a user adds or removes an emoji reaction on a message (the bot must be an admin in groups).
flow on_react on reaction {
when reaction.added {
send.text(`${reaction.user.first_name} reacted with ${reaction.emoji}`)
}
}
Scope: reaction.* β emoji, added (Boolean: true on add, false on remove), message_id, user.
See Messaging β react to set the bot's own reaction. Reference blueprint: reaction-inline-bot.botgami.
Inline modeβ
on inline_queryβ
Fires when a user types @yourbot <query> in any chat. Enable inline mode via @BotFather first, then answer with the answer_inline node.
flow inline on inline_query {
set { flow.results = [
{ id: "1", title: "Hello", text: "Hello there!", description: "A greeting" }
] }
answer_inline(inline.id, flow.results)
}
Scope: inline.* β id, query, offset, from.
Reference blueprints: reaction-inline-bot.botgami, toolbox-bot.botgami.