# How to set up the Telegram persistent keyboard?

The persistent keyboard is a set of buttons under the input field in Telegram. The customer sees them on every scenario step, including steps with an inline menu. For example: "Main menu", "☎ +380…" (the customer's number, so support can see it) and "Start". Inline buttons under messages stay as they were.

You build the buttons in a visual builder. You do not need to write JSON.

## When you need it

- The customer should get back to the main menu from anywhere in the scenario with one tap.
- Support needs to see the customer's number right in the conversation.
- You need a "Start" button that works exactly like a typed `/start`.

## Before you start

- [x] The `ff_persistent_reply_keyboard` flag is enabled on the instance. Without it the settings card is not shown. Ask support or your manager to enable it.
- [x] The scenario has a node with an alias that "Main menu" should lead to (for example, `main_menu`).
- [x] You know which scenario variable holds the customer's phone (for example, `phone`).
- [x] You can open and edit the process settings. No special rights are needed for this card.

## Step-by-step

### 0. Prepare the scenario: an alias for "Main menu"

The "Main menu" button leads to a "Connect To" node with an alias. If there is no such node, the button silently does nothing. Add it before you start, following [How to add an alias](#alias), and publish the scenario. If you will not have a "Main menu" button, skip this step.

### 1. Open the card

1. Log in to the constructor and click **"Process library"** in the left menu.
2. Open your process (the Telegram bot). The **"Setting of <process name>"** page opens.
3. Scroll to the **"Telegram persistent keyboard"** card.

You can also get there from Scenario Builder: the gear in the toolbar → **"Process settings"**.

No card on the page? The `ff_persistent_reply_keyboard` flag is not enabled (see "Before you start"). The Telegram channel must also be connected to this process.

<!-- screenshot: the "Telegram persistent keyboard" card with the switch on and the preview -->

### 2. Turn it on and fill in the buttons

1. Turn on the **"Enabled"** switch. The button blocks and **"What the customer will see"** appear.
2. Easiest: click **"Fill in an example"**. You get "Main menu", `☎ {{phone}}` and "Start". Then adjust them.
3. To add your own button: **"Add row"** → in the row, **"Button"**. Up to 4 rows and 3 buttons per row.
4. In **"Button text"** type the caption (up to 64 characters). To show customer data: **"Insert variable"** → type the variable name, for example `phone` → **"Insert"**. The text gets `{{phone}}`. Add a word or an icon next to it: `☎ {{phone}}`.
5. In **"What the button does"** choose an action (see the table below).
6. For "Go to a step (alias)", pick an alias in **"Scenario step"**, for example `main_menu`. Do not see it? Add it (step 0) and click **"Refresh the list"**; the **"Go to the scenario"** button opens the scenario.
7. In **"Line before the menu"** type a short line, for example `Menu is under the input field 👇`. An empty field means the bot sends just 👇.
8. Reorder rows with **"Move row up"** and **"Move row down"**. Remove extras with **"Delete button"** and **"Delete row"**.
9. Look at **"What the customer will see"**: these are the same buttons as the customer will see them. A button with a variable is shown with an example value.

After every step the change shows in **"What the customer will see"**. If the buttons have an error, saving shows a message (see "Common errors when saving").

### 3. Save

1. Click **"Save"**. You see a success message.
2. Wait up to a minute for the bot to pick up the change.

You can turn the keyboard off at any time: switch off **"Enabled"** and save. Turning it off always saves, even if the buttons have errors.

### 4. Check it in Telegram

1. Open your bot in Telegram and send `/start`. The buttons appear under the input field.
2. Tap "Main menu": the bot goes to the step with the alias.
3. Enter a phone number as the scenario asks: a "☎ +380…" button appears. Tap it: the number lands in the conversation and the bot stays silent.
4. Tap "Start": the bot starts the scenario over.

Did not work? See "Common errors when saving" and "What the customer may take for a bug" below.

### The three button actions

| What to choose in "What the button does" | What happens after a tap |
|---|---|
| **Go to a step (alias)** | The bot goes to the chosen scenario step. |
| **Start over (/start)** | Same as a typed `/start`: the bot starts over and forgets the entered data. |
| **Only send the text to the chat** | The button text appears in the conversation (support can see it). The bot does not reply and stays on the same step. |

A button whose variable is still empty is not shown. For example, `☎ {{phone}}` appears as soon as the customer enters the number. If no button can be shown at all, the bot works as before, without the keyboard.

<a id="alias"></a>

## How to add an alias for the "Main menu" button

The "Main menu" button leads to a "Connect To" node (a named entry point) with the alias you choose. If the scenario has no such node, the button silently does nothing. The card then shows the hint "This process's scenario has no aliases yet".

1. Open this process's scenario (the card has a **"Go to the scenario"** button).
2. At the start of the main menu add a **"Connect To"** node and type a name into the **"Alias"** field, for example `main_menu`.
3. Publish the scenario.
4. Come back to the card, press **"Refresh the list"** and pick this alias in **"Scenario step"**.

<!-- screenshot: the "Connect To" node with the alias main_menu in the Scenario Builder -->

## How to check it worked

1. Save and wait a minute.
2. Send `/start` to the bot in Telegram. The buttons appear under the input field.
3. Tap each button in turn:
   - "Main menu": the bot goes to the chosen step;
   - "☎ …": the number appears in the conversation, the bot stays silent;
   - "Start": the bot starts the scenario over.

## Common errors when saving

If the buttons have an error, the settings are not saved and an error message appears. Here is what the messages mean.

| Message | What to do |
|---|---|
| No more than 4 rows | Delete the extra row. |
| Row N: no more than 3 buttons | Move a button to another row or delete it. |
| Row N, button M: enter the button text | Fill in "Button text". |
| The text is longer than 64 characters | Shorten the text. |
| The text cannot be just a variable — add a word next to it | Instead of `{{phone}}` enter, for example, `☎ {{phone}}`. |
| Choose what the button does | Choose an action in "What the button does". |
| Choose a scenario step | For "Go to a step (alias)" pick a step from the list. |
| There is no step with the alias "…" in the scenario | Pick another step. Or add a node with this alias to the scenario and save the scenario. |
| This button already exists — texts must differ | Change the text of one of the buttons: identical captions are not allowed. |
| The line before the menu — no more than 200 characters | Shorten the line. |
| Add at least one button | Add a button or turn the keyboard off. |
| The persistent keyboard is turned off for this instance | Ask support to enable the `ff_persistent_reply_keyboard` flag. |
| You do not have enough rights to change the keyboard | Ask the administrator for access to this process. |
| Invalid JSON | Fix the JSON in "Advanced: JSON" or go back to the visual buttons. |

If an alias was removed from the scenario after a button was saved, the bot just skips that button. The rest of the keyboard still works.

## Line before the menu

An inline message (with buttons under the text) cannot bring the keyboard under the input field. So when the keyboard is not on screen and the bot's next message is inline, the bot first sends a short line and then the inline menu. This line appears only:

- on the first `/start` (and a typed `/start`) when the start step is an inline menu;
- after a broadcast;
- after the operator closes the dialog with a close button;
- after a step with its own reply keyboard;
- once a day, when the bot refreshes the keyboard.

The line is not in the dialog history in the operator panel.

## What removes the keyboard and when it comes back

The keyboard is not "pinned". With it enabled, a scenario step no longer removes it. Only the cases below can.

| Removed by | Comes back |
|---|---|
| A text broadcast | With the bot's first scenario reply, when the customer writes or taps a button. |
| A broadcast that jumps to a node (alias) | Right away, with that node's message. |
| The operator's close-dialog button | With the first message of the scenario's close branch, or with the bot's next reply. |
| A step with its own reply buttons (e.g. "Choose a city") | With the bot's next reply. If a step button has the same text as a persistent button, the step button wins. |
| The customer collapsed the keyboard | The bot does not expand it while the buttons stay the same. Once a day the bot may send it again, and then it expands. |

## Operator

- **Waiting for an operator (OP node with "continue scenario" checked).** Until the operator writes, the buttons work as usual.
- **The operator wrote.** The dialog belongs to the operator. Any tap, "Start" included, goes to them as plain text and the bot does not reply. An OP node without the checkbox connects the operator right away, so taps go to the operator from the OP node on.
- **Dialog closed.** The bot continues the scenario and the keyboard comes back with its first message.

## What the customer may take for a bug

- **"The bot sometimes sends an extra line 'Menu is under the input field 👇'".** That is the line before the menu: without it Telegram does not show the keyboard before an inline menu.
- **"The keyboard disappeared".** A broadcast, the operator's close button or a step with its own buttons removed it. It comes back with the bot's next reply.
- **"I collapsed it and it expanded by itself".** Once a day the bot refreshes the keyboard.
- **"I tapped 'Main menu' and the bot did not answer".** An operator is handling the dialog: the tap went to them as text.
- **"I tapped 'Start' and the phone button disappeared".** "Start" is a full `/start`: the bot forgets the number until the customer enters it again.
- **"I tapped ☎ and the bot is silent".** By design: the number appeared in the conversation for support.
- **"There is no phone button".** The bot does not know the customer's number yet.
- **"The list has a 'Main menu' item, but the main menu opened".** In a list with reply buttons (non-inline search_list) the persistent button wins over a list item with the same text.
- **During a survey (for example, CSAT)** a "Main menu" tap goes into the survey as an answer, and "Start" starts the scenario over.

## Limitations

- Telegram only, private chats only. Groups, Widget, Viber, WhatsApp, Instagram and e-chat get no keyboard.
- If the scenario calls the Telegram API itself (an "HTTP request" node with a Telegram URL, or an action jail script with its own request) and that call removes the keyboard, the bot does not see it. The keyboard comes back only with the next replacement or the daily refresh.
- At most 4 rows, 3 buttons per row, 64 characters in a button text, 200 characters in the line before the menu.

## Advanced: JSON

For advanced users: the **"Advanced: JSON"** section shows the same buttons as JSON. You can edit it directly, and the builder and the preview update. While the JSON is invalid, changes are not saved.

```json
[
  [
    { "label": "Main menu", "action": "goto_alias", "value": "main_menu" },
    { "label": "☎ {{phone}}", "action": "send_text" }
  ],
  [
    { "label": "Start", "action": "command", "value": "/start" }
  ]
]
```

Actions in JSON: `goto_alias` (needs `value` with an alias), `command` (only `/start`), `send_text` (no `value`).

## Related

- [Telegram keyboard button styling](/en/scenariodialog/explanation/telegram-keyboard-styling.md)
- [Inline keyboard in Telegram and Widget](/en/scenariodialog/explanation/inline-keyboard.md)
