Skip to main content

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​

TriggerFires when…Key context
on joinA user joins the chatmember.new_chat_member.user.*
on leaveA user leaves or is removedmember.new_chat_member.user.*
on join_requestA user requests to joinrequest.from.*, request.invite_link
on bot_addedThe bot is added to a chatβ€”
on bot_removedThe bot is removed from a chatβ€”
on member [filter]A membership sub-event occursmember.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.

TriggerFires when…Key scope
on business_connectionThe account connects or disconnects the botbusiness.connection_id, business.can_reply, business.is_enabled, business.user
on business_messageA customer DMs the connected accountbusiness.text, business.connection_id, business.from
on edited_business_messageA message in a connected chat is editedbusiness.* (new content)
on deleted_business_messagesMessages in a connected chat are deletedbusiness.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.

See also​