Variables

What are variables?

Variables are named memory slots that the bot stores separately for each user. You can use them to save user responses, API query results, payment data, and any intermediate values ​​within a scenario.

Variable values ​​persist across sessions—as long as the user is interacting with the bot, their variables remain active.

Syntax

You can insert a variable into any text field using double curly braces:

{{variable_name}}

Example:

Hello, {{name}}! Your order #{{order_id}} has been received.

Fallback operator ||

If a variable is not set, you can specify a default value using ||:

{{variable_name||default_value}}

Examples:

Your city: {{city||Not specified}}
Name: {{name||User}}
Email: {{email||address not entered}}

If the variable is set, its value is displayed. If not, the text following || is displayed. Without ||, an empty string is displayed if the variable is missing.

Variable autocomplete

In any text field within the builder, simply type {{ to bring up a list of available variables. Select the one you need from the list, and it will be inserted automatically.

Additionally, there is a [{}] button next to the fields; clicking it also opens the variable picker.

Dot notation — nested objects

Some variables contain objects with multiple fields. Use a dot to access a specific field:

{{payment.currency}}     → payment currency
{{result.text}}          → response text from OpenAI
{{sheets.Email}}         → value of the Email column from Google Sheets

Built-in Variables

The builder automatically provides a set of variables available in any node:

Variable Description
{{CHAT_ID}} User's Telegram chat ID
{{USER_MESSAGE}} Text of the user's last message (or media caption)
{{FILE_ID}} Telegram file_id of the last media file sent by the user
{{FILE_CAPTION}} Caption of the user's last media file
{{BOT_NAME}} Bot name
{{BOT_ID}} Bot's internal system ID
{{BOT_LINK}} Bot link (https://t.me/). Populated only if the bot has a username
{{BOT_LOGO}} Full URL of the bot's logo in public storage (e.g., https://example.com/storage/images/logo/123.jpg). If the bot has no logo, the placeholder remains unchanged
{{USER_MESSAGE_ID}} message_id of the incoming user message (for reply_to_message_id)
{{MESSAGE_THREAD_ID}} ID of the Telegram forum group thread (topic). Automatically populated if the message originated from a topic. Used in the "Forum thread ID" option of blocks to send a reply to the same topic.
{{FORWARD_ORIGIN_MESSAGE_ID}} ID of the original message in the channel (populated only if the user forwarded a message from a channel)
{{FORWARD_FROM_CHAT_ID}} ID of the source channel for the forwarded message (populated only for forwards from a channel)

How to set a variable

1. "Set Variables" block

Allows you to explicitly set a variable's value without user interaction. It supports the substitution of other variables and mathematical operations:

city = Moscow
full_name = {{first_name}} {{last_name}}
counter = {{counter}}+1
price_with_tax = {{price}}*1.2
avg = ({{a}}+{{b}})/2

If, after variable substitution, the "New Value" field contains only digits and the operators + - * / ( ), the result is automatically calculated and stored as a number (rounded to two decimal places). If the string contains text or the variable is empty, the value is stored as-is.

The rand function — random number

rand(min, max) returns a random integer in the range from min to max (inclusive). It can be used as a standalone value or within an arithmetic expression.

Example value Result
rand(0, 1) 0 or 1 (randomly)
rand(1, 6) a number from 1 to 6 (die roll)
rand(10, 99) a random two-digit number
rand(0, 2)*50 0, 50, or 100

Use case — random user distribution:

  1. Add a "Set Variables" block and create the variable group = rand(0, 1)
  2. Add a "Condition" block: if {{group}} = 0 → proceed to command A; otherwise → proceed to command B

Rounding functions

You can use rounding functions in expressions:

Function Description Example Result
round(x) Round to the nearest integer round({{price}}/3) 34 (if price=100)
round(x, N) Round to N decimal places round({{price}}/3, 1) 33.3
floor(x) Round down (integer part) floor({{credits}}/2) 5 (if credits=11)
ceil(x) Round up ceil({{credits}}/2) 6 (if credits=11)

Examples:

voice_credits = floor({{pay_credits}}/2)
video_credits = ceil({{pay_credits}}/3)
rounded_price = round({{total}}/{{count}}, 2)

The "strlen" function — string length

strlen(text) returns the number of characters in a string. It supports Unicode, so Russian and other characters are counted correctly.

Example value Variables Result
strlen({{user_name}}) user_name=John 4
strlen({{comment}}) comment=Hello world 11
strlen({{code}}) code=ABC 3

Usage examples:

name_len = strlen({{user_name}})
bio_len = strlen({{bio}})
total_chars = strlen({{a}})+strlen({{b}})

strlen can be combined with arithmetic operations: strlen({{name}})*2, strlen({{a}})+strlen({{b}}). ### 2. "Input Form" Block

When the bot is waiting for a user response, the entered text is automatically saved to the specified variable:

  • "Save to variable" field — the name of the variable where the input will be stored.
  • Example: the user enters their name → it is saved to {{name}}.

Skip button — when clicked, null is written to the variable, and the scenario proceeds to the next node.

Interrupt button — pressing this cancels the wait for input and deletes the message containing the form (in inline mode). In reply mode, the message is not deleted, but the wait state is cancelled. Script execution does not proceed further.

3. Integration blocks

After a request to OpenAI, Google Sheets, or another service is executed, the result is saved into variables with the specified prefix. See the Integrations section for details.

4. "API Request" block

The response from the external API is parsed and saved into variables. See the API Requests section for details.

5. Payment

Upon successful payment, transaction data is automatically saved into variables. See the Payments section for details.

Using variables in conditions

The "Condition" block checks a variable's value and routes the user along different branches of the script:

Operator Example
equals {{status}} equals active
does not equal {{age}} does not equal 0
contains {{text}} contains hello
greater than / less than {{score}} greater than 100
empty / not empty {{email}} is empty

Tips

  • Name variables using Latin characters without spaces: user_name, order_id.
  • Use meaningful names — this makes navigating the autocomplete list easier.
  • Remember that variables are strings; for numerical comparisons, ensure the value is actually a number.

Dynamic system parameters

The builder supports built-in dynamic parameters — special system values ​​calculated at the moment the bot executes. Their syntax resembles that of variables, but the name begins with $. ### {{$NOW}} — current date and time

$NOW returns the current server time. By default, it is in ISO 8601 format with a UTC offset:

2026-03-27T18:41:00+03:00

Without methods, {{$NOW}} uses the server's time zone. To specify a custom time zone or format, use a method chain.


Formatting methods

Methods are called using dot notation and can be chained together:

{{$NOW.setZone('Europe/Moscow').format('DD.MM.YYYY HH:mm')}}

Standard methods

Method Result
{{$NOW}} ISO 8601: 2026-03-27T18:41:00+03:00
{{$NOW.toDate()}} Date only: 2026-03-27
{{$NOW.toTime()}} 24-hour time: 18:41:00
{{$NOW.toTime12()}} 12-hour time: 6:41 PM
{{$NOW.toDateTime()}} Date and 24-hour time: 2026-03-27 18:41:00
{{$NOW.toDateTime12()}} Date and 12-hour time: 2026-03-27 6:41 PM
{{$NOW.toISO()}} ISO 8601 with UTC offset
{{$NOW.toTimestamp()}} Unix timestamp: 1743095260

Custom format — .format('...')

Use moment.js notation:

Token Meaning Example
YYYY 4-digit year 2026
YY Year (2 digits) 26
MM Month (01-12) 03
DD Day (01-31) 27
HH Hours (00-23) 18
hh Hours (01-12) 06
mm Minutes (00-59) 41
ss Seconds (00-59) 00
A AM/PM PM
a am/pm pm

Examples:

{{$NOW.format('DD.MM.YYYY')}}         → 27.03.2026
{{$NOW.format('DD.MM.YYYY HH:mm')}}   → 27.03.2026 18:41
{{$NOW.format('HH:mm')}}              → 18:41
{{$NOW.format('YYYY-MM-DD')}}         → 2026-03-27

Changing the time zone — .setZone('...')

Pass a standard IANA time zone name:

{{$NOW.setZone('Europe/Moscow')}}                        → ISO in MSK
{{$NOW.setZone('Europe/Moscow').toDate()}}               → date in MSK
{{$NOW.setZone('Europe/Moscow').toDateTime()}}           → date and time in MSK
{{$NOW.setZone('America/New_York').format('HH:mm')}}     → time in New York (HH:mm)

Date arithmetic — addDays, subDays, addMonths, subMonths, addHours, subHours

Shifts the point in time forward or backward. All methods are chainable—you can apply any formatting method after the shift.

Method Meaning
.addDays(N) Add N days
.subDays(N) Subtract N days
.addMonths(N) Add N months
.subMonths(N) Subtract N months
.addHours(N) Add N hours
.subHours(N) Subtract N hours
{{$NOW.addDays(1).toDate()}}                             → tomorrow (YYYY-MM-DD)
{{$NOW.subDays(1).toDate()}}                             → yesterday (YYYY-MM-DD)
{{$NOW.addDays(30).toDate()}}                            → in 30 days
{{$NOW.addMonths(1).toDate()}}                           → in 1 month
{{$NOW.addDays(30).toISO()}}                             → in 30 days (ISO, for ranges)
{{$NOW.setZone('Europe/Moscow').addDays(7).toDate()}}    → in 7 days (Moscow time)
{{$NOW.addDays(30).format('DD.MM.YYYY')}}                → in 30 days (DD.MM.YYYY)

Date range example (start: today, end: in a month):

Range start: {{$NOW}}
Range end:   {{$NOW.addDays(30).toISO()}}

Region Time Zone UTC
Moscow, St. Petersburg Europe/Moscow +3
Belarus Europe/Minsk +3
Ukraine Europe/Kiev +2/+3
Yekaterinburg Asia/Yekaterinburg +5
Novosibirsk Asia/Novosibirsk +7
Irkutsk Asia/Irkutsk +8
Vladivostok Asia/Vladivostok +10
Kazakhstan Asia/Almaty +5
Uzbekistan Asia/Tashkent +5
Georgia Asia/Tbilisi +4
Azerbaijan Asia/Baku +4
Armenia Asia/Yerevan +4
UAE Asia/Dubai +4
Turkey Europe/Istanbul +3
Germany Europe/Berlin +1/+2
UK Europe/London 0/+1
USA — East America/New_York -5/-4
USA — West America/Los_Angeles -8/-7
China Asia/Shanghai +8
Japan Asia/Tokyo +9

Using Autocomplete

In any builder field:

  1. Type {{$NOW — the variable picker will display all available options.
  2. Type {{$NOW. — the $NOW Methods popup will open, listing all methods and time zones.
  3. Select the desired option by clicking it or enter the expression manually.

Template Directives: @if and @foreach

You can use directives within message text for conditional output and array iteration.

@if — Conditional Output

@if(variable)
Text displayed if the condition is true
@endif

With an @else branch:

@if(status == 'ok')
Order accepted ✅
@else
An error occurred ❌
@endif

Supported Condition Formats

Format Description
@if(var) True if the variable is set and not empty
@if(!var) True if the variable is empty or not set
@if(var == 'value') Equality (numeric or string)
@if(var != 'value') Inequality
@if(var > 5) Greater than (numeric comparison)
@if(var >= 5) Greater than or equal to
@if(var < 5) Less than
@if(var <= 5) Less than or equal to
@if(var == 1 && var2 == 2) Logical AND — all conditions must be true
@if(var == 1 || var2 == 2) Logical OR — at least one condition must be true

Quotation marks around a string literal are optional: @if(status == ok) and @if(status == 'ok') are equivalent.

Examples:

@if(USER_ID > 0)
You are logged in.
@else
Access restricted.
@endif

@if(order.status == 'paid')
Thank you for your payment!
@endif

@if(!email)
You haven't provided an email.
@endif

@if(user_config.emotional_pair == 1 && user_config.pair_psy == 1)
Conditional message
@endif

@if(role == 'admin' || role == 'moderator')
Access granted.
@endif

You cannot mix && and || in a single expression: @if(a == 1 && b == 2 || c == 3) is not supported. Use multiple separate @if statements or break the logic down into "Condition" blocks.


@foreach — outputting an array

Used to output a list of elements from an array variable (e.g., the result of an API request):

@foreach(variable as element)
{{element}}
@endforeach

If the array contains objects, access their fields using dot notation:

@foreach(products as item)
• {{item.name}} — {{item.price}} RUB
@endforeach

@if works inside @foreach:

@foreach(orders as order)
@if(order.status == 'paid')
✅ {{order.id}}: paid
@else
⏳ {{order.id}}: pending
@endif
@endforeach

Edge Case Behavior

Situation Result
Variable is not an array The @foreach block outputs an empty string
Array is empty The @foreach block outputs an empty string
Variable in @if does not exist The condition evaluates to false (falsy)

Limitations

  • Nested @foreach blocks (a @foreach` inside another @foreach) are not supported.
  • Nested @if blocks (an @if inside another @if) are not supported. To check multiple conditions simultaneously, use && or || within a single @if.
  • Mixing && and || in a single expression is not supported — use the "Condition" block for complex logic.

Insertion via the Text Editor Toolbar

Two buttons have been added to the right side of the toolbar in the advanced text editor (accessed via the "⤢" button in the text block):

  • @if — inserts the @if(var) ... @endif template with the variable name highlighted for replacement.
  • @foreach — inserts the @foreach(items as item) ... @endforeach template with the array name highlighted.

After inserting the snippet, replace the highlighted text (the variable or array name) with the desired value.