
🤖 Viber чат-бот на PHP: надсилання структурованих повідомлень з клавіатурним меню
Користувач натискає кнопку в чаті й отримує не голий текст, а сітку з картинок, посилань і кольорових клавіш. Виглядає як нативний застосунок. Насправді це звичайний JSON, який ваш PHP-скрипт віддає через Viber REST API.
Проблема більшості посібників: вони вчать відповідати текстом на текст. Реальний бізнес-бот повинен показувати клавіатурне меню з кнопками-посиланнями, картинки та каруселі. Інакше користувач просто не зрозуміє, що бот уміє, і піде. З лютого 2024 року Viber перевів створення ботів на комерційну основу, 100 € на місяць через офіційних партнерів Rakuten Viber, а автентифікація переходить у HTTP-заголовок X-Viber-Auth-Token, а не в тіло запиту. Сніпети з auth_token всередині JSON більше не працюють.
Тут, робочий Viber-бот на PHP з нуля: від встановлення вебхука до надсилання структурованого клавіатурного меню з кнопками reply та open-url. Код актуальний для версії API 7.3.
💡 Швидкий огляд:
- Реєструємо комерційного бота через партнера Viber і отримуємо токен автентифікації
- Налаштовуємо вебхук через
set_webhookіз заголовкомX-Viber-Auth-Token - Приймаємо callback-події від Viber: webhook → subscribed → message
- Формуємо клавіатурне меню, сітку кнопок із кастомними кольорами, розмірами та діями
- Надсилаємо структуровану відповідь: текст, картинки, посилання, каруселі через
send_message
Як влаштований Viber Bot API
Viber Bot API працює за REST-моделлю: ваш сервер отримує callback-запити на вебхук і відповідає викликами до https://chatapi.viber.com/pa/send_message. Кожен запит автентифікується токеном у заголовку X-Viber-Auth-Token.
Базовий цикл виглядає так:
- Ви реєструєте бота через партнера Viber і отримуєте токен у панелі «Edit Info».
- Налаштовуєте вебхук, URL вашого сервера з валідним SSL (Let's Encrypt підходить, він у довіреному списку Java-сертифікатів Viber; самопідписані не приймаються).
- Viber надсилає POST-запити на цей URL за кожної події: користувач підписався, написав повідомлення, натиснув кнопку.
- Ваш PHP-скрипт читає вхідний JSON, розбирає поле
eventі відповідає викликомsend_message.
Перший крок для розуміння, подивитися на живого бота. Відкрийте Viber, у пошуку публічних акаунтів знайдіть бота будь-якого відомого бренду. Майже у кожного є чат-бот з меню. На скріншоті нижче, типовий результат пошуку публічного акаунта.

Після підписки ви потрапляєте в чат 1-на-1. Виглядає приблизно так: аватар, вітальне повідомлення, кнопка для старту діалогу.

Натисніть іконку повідомлення у правому верхньому куті та надішліть «Привіт». Якщо бот налаштований на клавіатурне меню, побачите відповідь із сіткою кнопок:

Це і є структуроване повідомлення. Кнопки бувають двох типів: reply повертають текст назад боту (натискання «Новини» або «Статті»), а open-url відкривають посилання в браузері. Тепер напишемо код, який таке віддає.
Крок 1: Отримуємо токен і налаштовуємо вебхук
Токен лежить у панелі адміністратора Viber: розділ Edit Info → поле App Key. Це рядок із літер і цифр, його ви підставляєте в кожен запит до API.
Вебхук встановлюється одним POST-запитом до https://chatapi.viber.com/pa/set_webhook. Тіло запиту, JSON з URL вашого обробника та списком подій, на які ви підписуєтеся. Заголовок, X-Viber-Auth-Token з вашим токеном.
Скрипт встановлення вебхука (збережіть як setup.php і запустіть один раз):
1 <?php 2 $token = 'ВАШ_X_VIBER_AUTH_TOKEN'; 3 4 $data = json_encode([ 5 'url' => 'https://your-domain.com/webhook.php', 6 'event_types' => ['message', 'subscribed', 'conversation_started'], 7 ]); 8 9 $ch = curl_init('https://chatapi.viber.com/pa/set_webhook'); 10 curl_setopt($ch, CURLOPT_POST, 1); 11 curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 12 curl_setopt($ch, CURLOPT_HTTPHEADER, [ 13 'Content-Type: application/json', 14 'X-Viber-Auth-Token: ' . $token, 15 ]); 16 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 17 18 $result = curl_exec($ch); 19 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 20 curl_close($ch); 21 22 echo "HTTP {$httpCode}: {$result}\n"; 23 24 if ($httpCode === 200) { 25 $response = json_decode($result, true); 26 if (($response['status'] ?? -1) === 0) { 27 echo "Вебхук установлен успешно.\n"; 28 } else { 29 echo "Ошибка Viber: " . ($response['status_message'] ?? 'неизвестная') . "\n"; 30 } 31 }
Що тут відбувається: cURL надсилає POST на set_webhook. Токен, строго в заголовку, не в тілі. Viber повертає {"status":0} у разі успіху й одразу ж надсилає callback webhook на вказаний URL, перевіряючи його доступність. Відповідь {"status":0,"status_message":"ok"} на цей callback є обов'язковою (розберемо в наступному кроці).
Якщо отримуєте invalidUrl, перевірте SSL-сертифікат. Let's Encrypt працює, самопідписаний, ні. На своєму сервері достатньо виконати certbot --nginx -d your-domain.com і налаштувати автооновлення через cron.
Крок 2: Приймаємо callback-події від Viber
Коли користувач взаємодіє з ботом, Viber надсилає на ваш вебхук POST-запит із JSON. Ваше завдання, прочитати поле event і зреагувати.
Мінімальний обробник webhook.php, який коректно відповідає на всі події:
1 <?php 2 $request = file_get_contents("php://input"); 3 $input = json_decode($request, true); 4 5 if ($input['event'] === 'webhook') { 6 $response = [ 7 'status' => 0, 8 'status_message' => 'ok', 9 'event_types' => ['delivered', 'seen', 'message', 'subscribed', 'conversation_started'], 10 ]; 11 echo json_encode($response); 12 exit; 13 } 14 15 if ($input['event'] === 'subscribed') { 16 $sender_id = $input['sender']['id']; 17 sendWelcomeMessage($sender_id); 18 } 19 20 if ($input['event'] === 'conversation_started') { 21 $sender_id = $input['sender']['id']; 22 sendMainMenu($sender_id); 23 } 24 25 if ($input['event'] === 'message') { 26 $type = $input['message']['type']; 27 $text = $input['message']['text']; 28 $sender_id = $input['sender']['id']; 29 $sender_name = $input['sender']['name']; 30 31 $data = match ($text) { 32 'News' => getNewsList($sender_id), 33 'Articles' => getArticleList($sender_id), 34 'Gallery' => getGalleryList($sender_id), 35 default => getMainMenu($sender_id), 36 }; 37 38 sendToViber($data); 39 }
Розбір подій:
webhook, надходить один раз під час встановлення вебхука. Обов’язково повернути{"status":0,"status_message":"ok"}із переліком підтримуваних подій. Без цього Viber не підтвердить URL, і бот не запуститься.subscribed, користувач підписався на бота. Ідеальний момент для надсилання вітального меню.conversation_started, користувач відкрив чат, уперше або повторно. Теж підходить для показу головного меню.message, основна робоча подія. Уtextлежить або довільне повідомлення користувача, абоActionBodyнатиснутої кнопки (якщо її типreply). Саме так бот розуміє, що обрали: «News», «Articles» чи «Gallery».
Конструкція match() доступна з PHP 8. Якщо у вас PHP 7.4, замініть на switch.
Крок 3: Формуємо клавіатурне меню
Клавіатура прикріплюється до будь-якого повідомлення через поле keyboard в JSON-об’єкті send_message. Це масив кнопок із роздільним налаштуванням ширини (Columns, 1-6), висоти (Rows, 1-2), кольору тла, тексту та дії.
Функція, що збирає головне меню із семи reply-кнопок та однієї кнопки-посилання:
1 <?php 2 3 function getMainMenu(string $user_id): array 4 { 5 $buttons = []; 6 7 $addReplyButton = function (string $label, string $actionBody, int $cols = 2, int $rows = 2) use (&$buttons) { 8 $buttons[] = [ 9 'Columns' => $cols, 10 'Rows' => $rows, 11 'Text' => $label, 12 'TextSize' => 'regular', 13 'TextVAlign' => 'bottom', 14 'TextHAlign' => 'center', 15 'TextOpacity' => 100, 16 'ActionType' => 'reply', 17 'ActionBody' => $actionBody, 18 'BgColor' => '#FFFFFF', 19 ]; 20 }; 21 22 $addReplyButton('NEWS', 'News'); 23 $addReplyButton('ARTICLES', 'Articles'); 24 $addReplyButton('INTERVIEWS', 'Interviews'); 25 $addReplyButton('GALLERY', 'Gallery'); 26 $addReplyButton('POLL', 'Poll'); 27 $addReplyButton('PLAYER OF THE MONTH','POTM'); 28 $addReplyButton('QUOTE OF THE DAY', 'Quote'); 29 30 $buttons[] = [ 31 'Columns' => 4, 32 'Rows' => 2, 33 'Text' => 'VISIT OUR WEBSITE', 34 'TextSize' => 'regular', 35 'TextVAlign' => 'bottom', 36 'TextHAlign' => 'center', 37 'TextOpacity' => 100, 38 'ActionType' => 'open-url', 39 'ActionBody' => 'https://your-site.com', 40 'BgColor' => '#FFFFFF', 41 ]; 42 43 return [ 44 'receiver' => $user_id, 45 'type' => 'text', 46 'text' => 'Please select one of the options below:', 47 'keyboard' => [ 48 'Type' => 'keyboard', 49 'BgColor' => '#FFFFFF', 50 'Buttons' => $buttons, 51 ], 52 ]; 53 }
Кнопки з Columns=2 і Rows=2, квадратні, по три в ряд (2+2+2=6). Кнопка-посилання з Columns=4 займає окремий рядок. Сітка збирається зліва направо, згори донизу. Сума Columns у ряді визначає розкладку.
Параметри кнопки:
Параметр | Значення | Призначення |
|---|---|---|
| 1-6 | Ширина кнопки в умовних колонках |
| 1-2 | Висота кнопки в рядах |
|
|
|
| рядок | Для |
| рядок | Напис на кнопці, підтримує HTML-теги b, i та font color |
| HEX | Колір тла кнопки |
| URL | Зображення поверх кнопки, JPEG, опціонально |
Крок 4: Надсилаємо дані через send_message
Функція-відправник, яку ви викликаєте з обробника подій:
1 <?php 2 3 function sendToViber(array $data): void 4 { 5 $token = 'ВАШ_X_VIBER_AUTH_TOKEN'; 6 7 $ch = curl_init('https://chatapi.viber.com/pa/send_message'); 8 curl_setopt($ch, CURLOPT_POST, 1); 9 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); 10 curl_setopt($ch, CURLOPT_HTTPHEADER, [ 11 'Content-Type: application/json', 12 'X-Viber-Auth-Token: ' . $token, 13 ]); 14 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 15 16 $result = curl_exec($ch); 17 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 18 curl_close($ch); 19 20 if ($httpCode !== 200) { 21 error_log("Viber API error: HTTP {$httpCode} - {$result}"); 22 } 23 }
Ключова відмінність від старих посібників: токен у заголовку, а не в тілі JSON. У тілі лише поля receiver, type, text, keyboard та опціональні sender.name, sender.avatar, tracking_data. Максимальний розмір JSON: 30 КБ. Перевищили, і Viber мовчки відкидає повідомлення, перевіряйте strlen(json_encode($data)) перед надсиланням.
Крок 5: Збираємо контент для кнопок
Коли користувач натискає reply-кнопку, її ActionBody відлітає на вебхук як текст повідомлення. Ви парсите його в match() і викликаєте потрібну функцію. Ось приклад для списку новин із зображеннями:
1 <?php 2 3 function getNewsList(string $user_id): array 4 { 5 $newsItems = [ 6 [ 7 'title' => 'Your Article Title', 8 'link' => 'https://your-site.com/article-1', 9 'image' => 'https://your-site.com/images/article-1.jpg', 10 ], 11 [ 12 'title' => 'Another Article', 13 'link' => 'https://your-site.com/article-2', 14 'image' => 'https://your-site.com/images/article-2.jpg', 15 ], 16 ]; 17 18 $buttons = []; 19 foreach ($newsItems as $item) { 20 $buttons[] = [ 21 'Columns' => 2, 22 'Rows' => 2, 23 'ActionType' => 'open-url', 24 'ActionBody' => $item['link'], 25 'BgColor' => '#FFFFFF', 26 'Image' => $item['image'], 27 ]; 28 $buttons[] = [ 29 'Columns' => 4, 30 'Rows' => 2, 31 'Text' => $item['title'], 32 'TextSize' => 'regular', 33 'TextHAlign' => 'left', 34 'TextVAlign' => 'top', 35 'ActionType' => 'open-url', 36 'ActionBody' => $item['link'], 37 'BgColor' => '#F5F5F5', 38 ]; 39 } 40 41 return [ 42 'receiver' => $user_id, 43 'type' => 'text', 44 'text' => 'Here are the latest updates:', 45 'keyboard' => [ 46 'Type' => 'keyboard', 47 'BgColor' => '#DDDDDD', 48 'Buttons' => $buttons, 49 ], 50 ]; 51 }
З боку користувача це виглядає як сітка карток: ліворуч зображення, праворуч заголовок. Натискання на будь-яку частину відкриває посилання в браузері.

Так само збираються галерея, список статей або опитування, змінюється лише вміст масиву $buttons і текст повідомлення. Якщо контенту багато і JSON наближається до 30 КБ, розбивайте на сторінки з кнопкою «Next».
Якщо вам зручніше працювати не з чистим API, а з готовою PHP-бібліотекою, подивіться viber-bot-php від Bogdaan, вона бере на себе валідацію підпису, роутинг подій і формування JSON.
Коротко: повний порядок запуску
- Отримайте токен у панелі Viber Admin Panel: розділ Edit Info → App Key.
- Викладіть обробник на сервер з HTTPS. Let's Encrypt підходить, самопідписаний сертифікат, ні. Viber звіряє сертифікат із довіреним списком Java Root CA.
- Встановіть вебхук POST-запитом до
https://chatapi.viber.com/pa/set_webhookз тілом{"url":"https://ваш-домен/webhook.php","event_types":[...]}та заголовкомX-Viber-Auth-Token. - Перевірте відповідь:
{"status":0}, успіх. Viber одразу надішле callbackwebhookна вашу URL. Переконайтеся, що скрипт повертає{"status":0,"status_message":"ok"}. - Надішліть перше меню: при події
conversation_startedвикличтеgetMainMenu()і передайте результат уsendToViber().
⁉️🤔 Часті запитання
Чи можна використовувати самопідписаний SSL-сертифікат для вебхука?
Ні. Viber перевіряє сертифікат за списком довірених кореневих центрів сертифікації Java. Самопідписаний сертифікат спричиняє помилку
invalidUrlпід час встановлення вебхука. Let's Encrypt у цьому списку є і він безплатний. На своєму сервері виконайтеcertbot --nginx -d your-domain.com, додайте автооновлення в cron, і Viber прийме сертифікат без проблем.
Чим відрізняється reply від open-url в ActionType?
replyнадсилаєActionBodyназад на вебхук як текст повідомлення. PHP-скрипт бачить його в$input['message']['text']і маршрутизує черезmatch(). Використовуйте для навігації всередині бота.open-urlвідкриває посилання у зовнішньому браузері, вебхук при цьому не смикається. На практиці меню комбінує обидва типи: 5-6 reply-кнопок для розділів і одну широку open-url-кнопку для переходу на сайт.
Які типи повідомлень підтримує Viber Bot API?
Viber Bot API підтримує дев'ять типів поля
typeуsend_message:text(звичайний текст),picture(JPEG-зображення),video(відеофайл),file(будь-який файл до 50 МБ),location(геоточка),contact(візитівка),sticker(стікер),rich_media(карусель карток) іurl(прев'ю посилання). Актуальний список з обов'язковими полями дивіться в документації Viber Developers Hub. Найпоширеніший сценарій:text+keyboardдля меню таpictureдля надсилання зображень. Каруселі гарні для вітрин і каталогів, але потребують більше коду.
Що робити, якщо користувач не бачить клавіатуру?
Три часті причини. Перша: JSON з клавіатурою перевищив 30 КБ, Viber мовчки відкидає такі повідомлення. Перевіряйте
strlen(json_encode($data))перед надсиланням. Друга: у відповіді наset_webhookне було"status":0, вебхук не встановлено, бот не отримує повідомлення. Третя: використовується старий формат зauth_tokenу тілі JSON. З API 7.0 це ігнорується, потрібен заголовокX-Viber-Auth-Token. Для налагодження увімкніть логування відповіді Viber API: він повертає читабельний JSON з кодом помилки invalidAuthToken, badData або missingData.
Чи можна змінити клавіатуру після надсилання?
Так. З кожним новим повідомленням можна надіслати іншу клавіатуру. Клієнт Viber завжди показує останню отриману. Це дозволяє будувати багаторівневі меню: головне меню → підменю розділу → конкретний контент. Кожен рівень зі своїм набором кнопок, і користувач переміщується між ними натисканнями reply-клавіш.
Скільки коштує запуск Viber-бота у 2026 році?
З 5 лютого 2024 року створення нового бота, 100 € на місяць через офіційних партнерів Rakuten Viber. Боти, створені до цієї дати, працюють за старими умовами. У вартість входить виділений акаунт із панеллю адміністратора, аналітика та доступ до всіх типів повідомлень, включно з rich_media і Viber Pay. Технічна частина API для комерційних і старих безплатних ботів ідентична.
Бот готовий: що далі
Клавіатурне меню — це лише вхідна точка. Коли базова механіка відпрацьована, додавайте каруселі контенту через rich_media, платежі через Viber Pay і сегментацію користувачів за тим, які кнопки вони натискають найчастіше. Кожен новий рівень не змінює основу: ви так само читаєте event, так само шлете send_message із заголовком X-Viber-Auth-Token.
Якщо потрібен живий приклад робочого коду, подивіться репозиторій viber-bot-php на GitHub, там розібрані всі події, підпис запитів і формування клавіатури з картинками. А для візуального старту ось півгодинний туторіал зі створення Viber-бота з нуля:
Головне, що варто запам'ятати: Viber Bot API простий на старті, але вимогливий до деталей. Правильний заголовок автентифікації, довірений SSL, валідний JSON до 30 КБ і чітка маршрутизація ActionBody, цього достатньо, щоб бот працював без збоїв. Беріть сніпети вище, підставляйте свій токен і домен, і запускайте перше меню сьогодні.



