Messages
Edit and delete
A practical guide to editing and deleting messages in the Telegram Bot API with PHP: working with editMessageText, editMessageReplyMarkup, editMessageCaption, and deleteMessage.
In bots, a dynamic interface is built not by sending dozens of new messages, but by changing existing ones. In the Telegram Bot API, a group of methods from the editMessage* family and the deleteMessage method are provided for this purpose. This allows updating order status, animating the processing, changing buttons on the fly, and keeping the user's chat clean without unnecessary spam.
editMessageText: changing message text
If a message was sent via sendMessage, its text and keyboard can be edited using the editMessageText method. Required parameters: text and one of the message identification methods: a pair of chat_id + message_id (for regular chats) or inline_message_id (for messages from inline mode — see section Inline mode).
We use the telegramApi() function that we wrote in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
$chatId = 123456789;
// 1. Отправляем начальное сообщение и сохраняем message_id
$sent = telegramApi($token, 'sendMessage', [
'chat_id' => $chatId,
'text' => 'Заявка #142: <b>создана</b>',
'parse_mode' => 'HTML',
]);
$messageId = $sent['result']['message_id'];
echo "Отправлено сообщение ID: {$messageId}\n";
// 2. Имитируем паузу в 2 секунды и редактируем текст
sleep(2);
$edited = telegramApi($token, 'editMessageText', [
'chat_id' => $chatId,
'message_id' => $messageId,
'text' => 'Заявка #142: <b>в работе</b>',
'parse_mode' => 'HTML',
]);
// Выводим ответ API в консоль для проверки
print_r($edited);
Example of a Telegram API response to a editMessageText call:
{
"ok": true,
"result": {
"message_id": 57,
"from": {
"id": 987654321,
"is_bot": true,
"first_name": "MyBot",
"username": "my_sample_bot"
},
"chat": {
"id": 123456789,
"first_name": "Ivan",
"type": "private"
},
"date": 1724501234,
"edit_date": 1724501236,
"text": "Заявка #142: в работе",
"entities": [
{
"offset": 13,
"length": 8,
"type": "bold"
}
]
}
}JSON response breakdown: The ok: true field confirms the successful execution of the request. The result key contains the actual data of the Message object. Pay attention to the appearance of the edit_date field — this is the UNIX-timestamp of the last edit time. The entities field confirms that the HTML markup was applied correctly (characters 13 to 21 became bold).
editMessageReplyMarkup: changing only the keyboard
When the message text does not need to be changed, but you need to add, update, or completely remove inline buttons below it, use the editMessageReplyMarkup method. This saves traffic and avoids reformatting the main text.
We use the telegramApi() function that we created in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
$chatId = 123456789;
$messageId = 57;
// Добавляем или обновляем клавиатуру под сообщением
telegramApi($token, 'editMessageReplyMarkup', [
'chat_id' => $chatId,
'message_id' => $messageId,
'reply_markup' => [
'inline_keyboard' => [
[
['text' => '✅ Подтвердить', 'callback_data' => 'confirm_142'],
['text' => '❌ Отклонить', 'callback_data' => 'cancel_142'],
],
],
],
]);
// Чтобы полностью удалить клавиатуру — передаём пустой массив inline_keyboard
telegramApi($token, 'editMessageReplyMarkup', [
'chat_id' => $chatId,
'message_id' => $messageId,
'reply_markup' => [
'inline_keyboard' => [],
],
]);
editMessageCaption: captions for photos and documents
For media messages (images, files, videos sent via methods from chapter Фото, документы, media group), the text cannot be changed using the editMessageText method — the API will return an error. The editMessageCaption method is intended for them.
We use the telegramApi() function from chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
$chatId = 123456789;
$photoMessageId = 58;
$response = telegramApi($token, 'editMessageCaption', [
'chat_id' => $chatId,
'message_id' => $photoMessageId,
'caption' => 'Новая подпись к фото с <b>HTML-разметкой</b>',
'parse_mode' => 'HTML',
]);
var_dump($response['ok']); // bool(true)
deleteMessage: deleting messages
The deleteMessage method permanently deletes a message from the chat. The bot can reliably delete its own messages. The bot can only delete user messages in groups and channels if it has administrator rights can_delete_messages.
We use the telegramApi() function created in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
$chatId = 123456789;
$messageId = 57;
$result = telegramApi($token, 'deleteMessage', [
'chat_id' => $chatId,
'message_id' => $messageId,
]);
// При успехе Telegram возвращает boolean true в поле result
var_dump($result['result']); // bool(true)
The API response for a successful message deletion looks like this:
{
"ok": true,
"result": true
}Common errors and their handling
When actively changing messages, the Telegram Bot API may return errors with HTTP code 400. Let's look at the most common ones:
- Bad Request: message is not modified — an attempt to write exactly the same content to the text or keyboard that is already there. The error is not critical, it should be ignored.
- Bad Request: message can't be edited — more than 48 hours have passed since the message was sent, or the bot is trying to edit someone else's message.
- Bad Request: message to edit not found — an non-existent
message_idis specified or the message has already been deleted by the user. - Bad Request: message can't be deleted — the message is too old or the bot does not have permission to delete messages in this chat.
Safe wrapper safeEditMessageText
To prevent the bot's script from breaking when users repeatedly click inline buttons, we wrap editMessageText in a safe function with exception handling:
We use the telegramApi() function implemented in chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.
<?php
function safeEditMessageText(string $token, int $chatId, int $messageId, string $text): bool {
try {
telegramApi($token, 'editMessageText', [
'chat_id' => $chatId,
'message_id' => $messageId,
'text' => $text,
'parse_mode' => 'HTML',
]);
return true;
} catch (RuntimeException $e) {
$errorMessage = $e->getMessage();
// Если контент не изменился — считаем это успехом
if (str_contains($errorMessage, 'message is not modified')) {
return true;
}
// Если отредактировать нельзя (прошло 48ч) — удаляем старое и отправляем новое
if (str_contains($errorMessage, "message can't be edited")) {
try {
telegramApi($token, 'deleteMessage', [
'chat_id' => $chatId,
'message_id' => $messageId,
]);
} catch (Exception $ignored) {}
telegramApi($token, 'sendMessage', [
'chat_id' => $chatId,
'text' => $text,
'parse_mode' => 'HTML',
]);
return true;
}
throw $e;
}
}
Practical pattern: Interactive order status
Below is a complete PHP script that demonstrates the "one live message" pattern. The bot publishes an order, changes its status step by step, and neatly removes the buttons at the end of the procedure.
We use the telegramApi() function from chapter Первый запрос: getMe. Here we only call it — we do not copy the implementation.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
$managerChatId = 123456789;
$orderId = 5081;
// Шаг 1: Создаём сообщение о новом заказе с кнопкой
$response = telegramApi($token, 'sendMessage', [
'chat_id' => $managerChatId,
'text' => "📦 <b>Заказ #{$orderId}</b>\nСтатус: <i>Новый</i>",
'parse_mode' => 'HTML',
'reply_markup' => [
'inline_keyboard' => [
[['text' => '🚀 Взять в работу', 'callback_data' => "process_{$orderId}"]],
],
],
]);
$messageId = $response['result']['message_id'];
echo "Сообщение создано: ID {$messageId}\n";
// Симулируем паузу на реакцию менеджера
sleep(3);
// Шаг 2: Менеджер взял заказ — обновляем текст и меняем кнопку
telegramApi($token, 'editMessageText', [
'chat_id' => $managerChatId,
'message_id' => $messageId,
'text' => "📦 <b>Заказ #{$orderId}</b>\nСтатус: ⚙️ <i>В сборке</i>\nОтветственный: Алексей",
'parse_mode' => 'HTML',
'reply_markup' => [
'inline_keyboard' => [
[['text' => '✅ Завершить заказ', 'callback_data' => "finish_{$orderId}"]],
],
],
]);
echo "Статус обновлен: В сборке\n";
// Симулируем процесс сборки
sleep(3);
// Шаг 3: Заказ выполнен — обновляем текст и удаляем клавиатуру
telegramApi($token, 'editMessageText', [
'chat_id' => $managerChatId,
'message_id' => $messageId,
'text' => "📦 <b>Заказ #{$orderId}</b>\nСтатус: ✅ <b>Выполнен</b>\nВремя: " . date('H:i:s'),
'parse_mode' => 'HTML',
]);
telegramApi($token, 'editMessageReplyMarkup', [
'chat_id' => $managerChatId,
'message_id' => $messageId,
'reply_markup' => [
'inline_keyboard' => [],
],
]);
echo "Заказ завершен, кнопки удалены.\n";
Next: let's move on to a detailed analysis of button creation in chapter Inline Keyboard.
"