Keyboards

Reply keyboard

A complete guide to Reply Keyboard in the Telegram Bot API on PHP. We cover ReplyKeyboardMarkup parameters, sending buttons, handling contacts and location, and removing the keyboard.

Reply Keyboard (a regular or "template" keyboard) is a Telegram interface element that replaces the user's standard system keyboard with a set of pre-configured buttons. Unlike Inline Keyboard, these buttons are not attached to specific messages, but are located at the bottom of the screen, right below the text input field.

How it works and differences from Inline Keyboard

The main architectural difference of the Reply Keyboard lies in the way data is transmitted to your server:

  • When a Reply Keyboard button is pressed, the Telegram client automatically sends a regular text message to the chat on behalf of the user. The text of this message exactly matches the label on the button.
  • Your bot receives a standard Update object with the type message. No callback_query events are generated in this case.
  • The user can hide the keyboard at any time and type the same text manually — for your server-side handler, the result will be absolutely identical.

Reply Keyboard is ideal for building the bot's main menu ("Catalog", "Profile", "Cart"), quickly choosing answers in polls, or securely requesting sensitive data (phone number, geolocation).

ReplyKeyboardMarkup Parameters

The keyboard is passed in the reply_markup parameter of the sendMessage method (or any other media sending method) as a JSON string. The keyboard object supports the following settings:

  • keyboard (array of arrays of KeyboardButton objects) — a two-dimensional grid of buttons. Each nested array represents a single horizontal row.
  • resize_keyboard (boolean) — defaults to false. If left disabled, the buttons will be huge (as tall as the phone's standard keyboard). Always set true so that the buttons shrink neatly to fit the text size.
  • one_time_keyboard (boolean) — if true, the keyboard will automatically hide immediately after any button is pressed. It can be brought back by tapping a special icon in the input field.
  • input_field_placeholder (string) — placeholder text displayed in the input field when the keyboard is active (e.g., "Choose a menu item..."). Maximum 64 characters.
  • is_persistent (boolean) — if true, the keyboard will always be displayed, even when the standard system keyboard is open for text input.
  • selective (boolean) — allows showing the keyboard only to specific users. Useful in group chats to avoid spamming the interface for all participants.

Sending Reply Keyboard in PHP

We use the telegramApi() function that we created in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.

<?php
// send_keyboard.php

// ID чата, куда отправляем клавиатуру.
// Значение chat_id мы научились получать в главе https://api.telegram.org/ или в разделе про webhook.
$chatId = 123456789;

// Описываем структуру клавиатуры
$keyboardMarkup = [
'keyboard' => [
[
['text' => '🛍️ Каталог'],
['text' => '🛒 Корзина']
],
[
['text' => '📱 Поделиться контактом', 'request_contact' => true],
['text' => '📍 Отправить локацию', 'request_location' => true]
],
[
['text' => '❓ Помощь']
]
],
'resize_keyboard' => true,
'one_time_keyboard' => false,
'input_field_placeholder' => 'Выберите действие...'
];

try {
$response = telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Добро пожаловать в наше меню! Используйте кнопки ниже для навигации.',
'reply_markup' => json_encode($keyboardMarkup)
]);

echo "Запрос успешно выполнен. Результат:\n";
print_r($response);
} catch (Exception $e) {
echo "Произошла ошибка: " . $e->getMessage() . "\n";
}

Analyzing a Successful API Response

Upon successfully sending a message with a keyboard, Telegram returns a standard Message object. Note that the keyboard structure itself is not duplicated in the response — the API only confirms that the text message was sent:

{
"ok": true,
"result": {
"message_id": 1024,
"from": {
"id": 987654321,
"is_bot": true,
"first_name": "My Helper Bot",
"username": "my_helper_bot"
},
"chat": {
"id": 123456789,
"first_name": "Ivan",
"type": "private"
},
"date": 1672531199,
"text": "Добро пожаловать в наше меню! Используйте кнопки ниже для навигации."
}
}

If the ok field in the response is false, be sure to check description. The most common error when sending keyboards is invalid JSON in the reply_markup parameter (for example, forgetting to call json_encode or passing an incorrect array structure).

Handling Clicks on the Server

When a user presses a button, your handler script receives a regular message. To parse incoming updates, we use the logic described in chapter Webhook.

To see the structure of the incoming request "from the inside", you can write the raw stream to a file using file_put_contents('update.json', file_get_contents('php://input')). Below is an example of handling text buttons, as well as special buttons for sending a contact and geolocation.

We use the telegramApi() function that we created in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.

<?php
// webhook_handler.php

$rawInput = file_get_contents('php://input');
$update = json_decode($rawInput, true);

if (isset($update['message'])) {
$message = $update['message'];
$chatId = $message['chat']['id'];

// 1. Обработка текстовых кнопок
if (isset($message['text'])) {
$text = $message['text'];

switch ($text) {
case '🛍️ Каталог':
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Загружаю актуальный список товаров...'
]);
break;

case '🛒 Корзина':
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Ваша корзина пуста. Добавьте товары из каталога.'
]);
break;

case '❓ Помощь':
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Вы можете управлять ботом с помощью кнопок меню.'
]);
break;
}
}

// 2. Обработка кнопки отправки контакта
if (isset($message['contact'])) {
$contact = $message['contact'];
$phoneNumber = $contact['phone_number'];
$firstName = $contact['first_name'];

// Безопасность: проверяем, совпадает ли user_id контакта с id отправителя,
// чтобы пользователь не прислал чужой контакт из телефонной книги.
if (isset($contact['user_id']) && $contact['user_id'] === $message['from']['id']) {
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => "Спасибо, {$firstName}! Ваш номер {$phoneNumber} успешно верифицирован."
]);
} else {
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Пожалуйста, отправьте именно свой контакт через кнопку.'
]);
}
}

// 3. Обработка кнопки отправки геолокации
if (isset($message['location'])) {
$location = $message['location'];
$latitude = $location['latitude'];
$longitude = $location['longitude'];

telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => "Координаты приняты! Широта: {$latitude}, Долгота: {$longitude}"
]);
}
}

How to Remove the Keyboard

The Reply Keyboard does not disappear on its own (even if the one_time_keyboard parameter is enabled, it only collapses into an icon). To completely remove it from the user's screen, you must send a special ReplyKeyboardRemove object in the reply_markup parameter of the next message.

We use the telegramApi() function that we created in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.

<?php
// remove_keyboard.php

$chatId = 123456789;

$removeMarkup = [
'remove_keyboard' => true,
'selective' => false
];

try {
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => 'Переходим в стандартный режим ввода. Клавиатура скрыта.',
'reply_markup' => json_encode($removeMarkup)
]);
echo "Клавиатура успешно удалена из интерфейса чата.\n";
} catch (Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
}

Important Limitations and Features

  • No hidden data: Unlike Inline buttons, which have a hidden callback_data parameter, regular buttons only send visible text. If you change the button name (for example, fix a typo), the old PHP handler will stop responding to it. You will have to change the conditions in switch.
  • Localization issue: If your bot is multi-lingual, you will have to check the button text in all supported languages or use FSM (конечные автоматы) to track the user's current context.
  • Interface overlap in groups: In group chats, the Reply Keyboard can interfere with communication. Use the selective => true parameter so that the keyboard only appears for the user who triggered the command.

Next: Команды /start, /help