Skip to main content

Your First Flow

A flow is a named block of logic that runs when a trigger fires. Let's build one up step by step, then add a menu, a button handler, and a variable.

Step 1: Respond to /start

bot FirstFlow {
flow start on command "/start" {
send.text("Hello! 👋")
}
}

flow start on command "/start" says: when a user sends /start, run this body. The body sends one message.

Step 2: Declare a menu

Menus are declared once at the top of the bot block and attached to any message with reply_markup. Each button calls a subflow handler:

bot FirstFlow {
menu startMenu {
text "🎉 Surprise me" -> doSurprise()
}

flow start on command "/start" {
send.text("Hello! 👋", reply_markup: startMenu)
}
}

The menu lives next to your bot definition. You can attach it to any send.* or edit.* call.

Step 3: Handle the button press

Add the handler subflow. The menu routes each press here automatically:

subflow doSurprise() {
send.text("🎉 Surprise! Thanks for tapping.")
}

The full bot so far:

bot FirstFlow {
menu startMenu {
text "🎉 Surprise me" -> doSurprise()
}

flow start on command "/start" {
send.text("Hello! 👋", reply_markup: startMenu)
}

subflow doSurprise() {
send.text("🎉 Surprise! Thanks for tapping.")
}
}

Step 4: Add a variable

Variables hold state. A user.* variable persists per user across sessions. Declare it at the top of the bot block, then write to it inside a set { ... } block.

bot FirstFlow {
user taps: Number = 0

menu startMenu {
text "🎉 Surprise me" -> doSurprise()
}

flow start on command "/start" {
send.text("Hello! 👋", reply_markup: startMenu)
}

subflow doSurprise() {
set { user.taps += 1 }
send.text(`🎉 You've tapped me ${user.taps} time(s)!`)
}
}

Note the two ways values appear:

  • set { user.taps += 1 } — write a variable (here, increment by 1).
  • `... ${user.taps} ...` — read a variable inside a backtick template.

Step 5: Show current state in the menu

Because menus re-render from current state on every press, you can show a live counter directly in a button label:

menu startMenu {
text `🎉 Tap (${user.taps})` -> doSurprise()
}

Every press updates user.taps, then re-renders the menu — the label stays in sync automatically.

Step 6: Redeploy

Click Publish again. Every time you change your .botgami and publish, the live bot picks up the new logic. Existing user.* values are preserved.

Recap

PieceWhat it does
flow name on command "/start"Runs on a slash command
menu name { text "Label" -> handler() }Declares a named keyboard
send.text(..., reply_markup: menuName)Attaches the menu to a message
subflow name() { ... }Handles a button press
user x: Number = 0Declares persistent per-user state
set { user.x += 1 }Writes a variable
`${user.x}`Reads a variable into text

Next