
🤖 Viber chatbot w PHP: wysyłanie ustrukturyzowanych wiadomości z menu klawiatury
Użytkownik klika przycisk na czacie i zamiast surowego tekstu otrzymuje siatkę obrazków, linków i kolorowych klawiszy. Wygląda to jak natywna aplikacja. W rzeczywistości to zwykły JSON, który Pana/Pani skrypt PHP zwraca przez Viber REST API.
Problem większości poradników: uczą odpowiadać tekstem na tekst. Prawdziwy bot biznesowy musi pokazywać menu klawiaturowe z przyciskami-linkami, obrazki i karuzele. W przeciwnym razie użytkownik po prostu nie zrozumie, co bot potrafi, i odejdzie. Od lutego 2024 roku Viber przeniósł tworzenie botów na zasadach komercyjnych, 100 € miesięcznie przez oficjalnych partnerów Rakuten Viber, a uwierzytelnianie przechodzi do nagłówka HTTP X-Viber-Auth-Token, a nie do treści żądania. Fragmenty kodu z auth_token wewnątrz JSON-a już nie działają.
Oto działający bot Viber w PHP od zera: od ustawienia webhooka do wysłania ustrukturyzowanego menu klawiaturowego z przyciskami reply i open-url. Kod jest aktualny dla wersji API 7.3.
💡 Szybki przegląd:
- Rejestrujemy komercyjnego bota przez partnera Viber i otrzymujemy token uwierzytelniający
- Konfigurujemy webhook przez
set_webhookz nagłówkiemX-Viber-Auth-Token - Odbieramy zdarzenia callback od Viber: webhook → subscribed → message
- Tworzymy menu klawiaturowe, siatkę przycisków z niestandardowymi kolorami, rozmiarami i akcjami
- Wysyłamy ustrukturyzowaną odpowiedź: tekst, obrazki, linki, karuzele przez
send_message
Jak działa Viber Bot API
Viber Bot API działa w modelu REST: Pana/Pani serwer otrzymuje żądania callback na webhook i odpowiada wywołaniami do https://chatapi.viber.com/pa/send_message. Każde żądanie jest uwierzytelniane tokenem w nagłówku X-Viber-Auth-Token.
Podstawowy cykl wygląda następująco:
- Rejestruje Pan/Pani bota przez partnera Viber i otrzymuje token w panelu „Edit Info".
- Konfiguruje Pan/Pani webhook, URL Pana/Pani serwera z ważnym SSL (Let's Encrypt jest odpowiedni, znajduje się na liście zaufanych certyfikatów Java Viber; samopodpisane nie są akceptowane).
- Viber wysyła żądania POST na ten URL przy każdym zdarzeniu: użytkownik zasubskrybował, napisał wiadomość, kliknął przycisk.
- Pana/Pani skrypt PHP odczytuje przychodzący JSON, analizuje pole
eventi odpowiada wywołaniemsend_message.
Pierwszy krok do zrozumienia to spojrzeć na żywego bota. Proszę otworzyć Viber, w wyszukiwarce kont publicznych znaleźć bota dowolnej znanej marki. Prawie każdy ma chatbota z menu. Na zrzucie ekranu poniżej typowy wynik wyszukiwania konta publicznego.

Po subskrypcji trafia Pan/Pani na czat 1-na-1. Wygląda to mniej więcej tak: awatar, wiadomość powitalna, przycisk do rozpoczęcia dialogu.

Proszę kliknąć ikonę wiadomości w prawym górnym rogu i wysłać „Cześć". Jeśli bot jest skonfigurowany z menu klawiaturowym, zobaczy Pan/Pani odpowiedź z siatką przycisków:

To jest właśnie ustrukturyzowana wiadomość. Przyciski są dwóch typów: reply zwracają tekst z powrotem do bota (kliknięcie „Aktualności" lub „Artykuły"), a open-url otwierają link w przeglądarce. Teraz napiszemy kod, który to generuje.
Krok 1: Otrzymujemy token i konfigurujemy webhook
Token znajduje się w panelu administratora Viber: sekcja Edit Info → pole App Key. To ciąg liter i cyfr, który wstawia Pan/Pani do każdego żądania API.
Webhook ustawia się jednym żądaniem POST do https://chatapi.viber.com/pa/set_webhook. Treść żądania to JSON z URL-em Pana/Pani handlera i listą zdarzeń, które Pan/Pani subskrybuje. Nagłówek to X-Viber-Auth-Token z Pana/Pani tokenem.
Skrypt instalacji webhooka (proszę zapisać jako setup.php i uruchomić raz):
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 }
Co tu się dzieje: cURL wysyła POST do set_webhook. Token, ściśle w nagłówku, nie w treści. Viber zwraca {"status":0} w przypadku sukcesu i natychmiast wysyła callback webhook na wskazany URL, sprawdzając jego dostępność. Odpowiedź {"status":0,"status_message":"ok"} na ten callback jest obowiązkowa (omówimy to w następnym kroku).
Jeśli otrzymuje Pan/Pani invalidUrl, proszę sprawdzić certyfikat SSL. Let's Encrypt działa, samopodpisany nie. Na swoim serwerze wystarczy wykonać certbot --nginx -d your-domain.com i skonfigurować automatyczne odnawianie przez cron.
Krok 2: Odbieramy zdarzenia callback od Viber
Gdy użytkownik wchodzi w interakcję z botem, Viber wysyła na Pana/Pani webhook żądanie POST z JSON-em. Pana/Pani zadaniem jest odczytanie pola event i zareagowanie.
Minimalny handler webhook.php, który poprawnie odpowiada na wszystkie zdarzenia:
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 }
Analiza zdarzeń:
webhook, przychodzi raz podczas ustawiania webhooka. Należy obowiązkowo zwrócić{"status":0,"status_message":"ok"}z listą obsługiwanych zdarzeń. Bez tego Viber nie potwierdzi URL-a i bot nie wystartuje.subscribed, użytkownik zasubskrybował bota. Idealny moment na wysłanie menu powitalnego.conversation_started, użytkownik otworzył czat, po raz pierwszy lub ponownie. Również nadaje się do wyświetlenia menu głównego.message, główne zdarzenie robocze. Wtextznajduje się albo dowolna wiadomość użytkownika, alboActionBodywciśniętego przycisku (jeśli jego typ toreply). Właśnie w ten sposób bot rozumie, co wybrano: „News", „Articles" czy „Gallery".
Konstrukcja match() jest dostępna od PHP 8. Jeśli korzystają Państwo z PHP 7.4, proszę zastąpić ją switch.
Krok 3: Tworzymy menu klawiaturowe
Klawiaturę dołącza się do dowolnej wiadomości przez pole keyboard w obiekcie JSON send_message. Jest to tablica przycisków z osobnym ustawieniem szerokości (Columns, 1-6), wysokości (Rows, 1-2), koloru tła, tekstu i akcji.
Funkcja budująca menu główne z siedmioma przyciskami reply i jednym przyciskiem-linkiem:
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 }
Przyciski z Columns=2 i Rows=2, kwadratowe, po trzy w rzędzie (2+2+2=6). Przycisk-link z Columns=4 zajmuje osobny wiersz. Siatka jest układana od lewej do prawej, z góry na dół. Suma Columns w rzędzie określa układ.
Parametry przycisku:
Parametr | Wartość | Przeznaczenie |
|---|---|---|
| 1-6 | Szerokość przycisku w umownych kolumnach |
| 1-2 | Wysokość przycisku w rzędach |
|
|
|
| ciąg znaków | Dla |
| ciąg znaków | Napis na przycisku, obsługuje znaczniki HTML b, i oraz font color |
| HEX | Kolor tła przycisku |
| URL | Obrazek na przycisku, JPEG, opcjonalnie |
Krok 4: Wysyłamy dane przez send_message
Funkcja wysyłająca, którą wywołują Państwo z handlera zdarzeń:
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 }
Kluczowa różnica w stosunku do starszych poradników: token w nagłówku, a nie w treści JSON. W treści tylko pola receiver, type, text, keyboard oraz opcjonalne sender.name, sender.avatar, tracking_data. Maksymalny rozmiar JSON-a: 30 KB. Po przekroczeniu Viber po cichu odrzuca wiadomość, proszę sprawdzać strlen(json_encode($data)) przed wysłaniem.
Krok 5: Budujemy treść dla przycisków
Gdy użytkownik naciśnie przycisk reply, jego ActionBody trafia na webhook jako tekst wiadomości. Parsują go Państwo w match() i wywołują odpowiednią funkcję. Oto przykład dla listy newsów z obrazkami:
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 }
Z perspektywy użytkownika wygląda to jak siatka kart: po lewej obrazek, po prawej nagłówek. Kliknięcie w dowolną część otwiera link w przeglądarce.

W ten sam sposób buduje się galerię, listę artykułów czy ankietę, zmienia się tylko zawartość tablicy $buttons i tekst wiadomości. Jeśli treści jest dużo i JSON zbliża się do 30 KB, proszę podzielić ją na strony z przyciskiem „Next".
Jeśli wygodniej pracuje się Państwu nie z czystym API, a z gotową biblioteką PHP, proszę spojrzeć na viber-bot-php od Bogdaan, bierze ona na siebie walidację podpisu, routing zdarzeń i tworzenie JSON-a.
Krótko: pełna kolejność uruchamiania
- Proszę uzyskać token w panelu Viber Admin Panel: sekcja Edit Info → App Key.
- Proszę umieścić handler na serwerze z HTTPS. Let's Encrypt jest odpowiedni, certyfikat z podpisem własnym nie. Viber weryfikuje certyfikat z zaufaną listą Java Root CA.
- Proszę ustawić webhook żądaniem POST do
https://chatapi.viber.com/pa/set_webhookz treścią{"url":"https://ваш-домен/webhook.php","event_types":[...]}i nagłówkiemX-Viber-Auth-Token. - Proszę sprawdzić odpowiedź:
{"status":0}, sukces. Viber natychmiast wyśle callbackwebhookna Pana/Pani URL. Proszę upewnić się, że skrypt zwraca{"status":0,"status_message":"ok"}. - Proszę wysłać pierwsze menu: przy zdarzeniu
conversation_startedproszę wywołaćgetMainMenu()i przekazać wynik dosendToViber().
⁉️🤔 Często zadawane pytania
Czy można użyć certyfikatu SSL z podpisem własnym dla webhooka?
Nie. Viber sprawdza certyfikat według listy zaufanych głównych urzędów certyfikacji Java. Certyfikat z podpisem własnym powoduje błąd
invalidUrlpodczas ustawiania webhooka. Let's Encrypt znajduje się na tej liście i jest bezpłatny. Na swoim serwerze proszę wykonaćcertbot --nginx -d your-domain.com, dodać automatyczne odnowienie w cronie, a Viber zaakceptuje certyfikat bez problemów.
Czym różni się reply od open-url w ActionType?
replywysyłaActionBodyz powrotem na webhook jako tekst wiadomości. Skrypt PHP widzi go w$input['message']['text']i routuje przezmatch(). Proszę używać do nawigacji wewnątrz bota.open-urlotwiera link w zewnętrznej przeglądarce, webhook nie jest przy tym wywoływany. W praktyce menu łączy oba typy: 5-6 przycisków reply do sekcji i jeden szeroki przycisk open-url do przejścia na stronę.
Jakie typy wiadomości obsługuje Viber Bot API?
Viber Bot API obsługuje dziewięć typów pola
typewsend_message:text(zwykły tekst),picture(obraz JPEG),video(plik wideo),file(dowolny plik do 50 MB),location(punkt geograficzny),contact(wizytówka),sticker(naklejka),rich_media(karuzela kart) iurl(podgląd linku). Aktualną listę z wymaganymi polami można znaleźć w dokumentacji Viber Developers Hub. Najczęstszy scenariusz:text+keyboarddo menu orazpicturedo wysyłania obrazów. Karuzele są dobre do witryn sklepowych i katalogów, ale wymagają więcej kodu.
Co zrobić, jeśli użytkownik nie widzi klawiatury?
Trzy częste przyczyny. Pierwsza: JSON z klawiaturą przekroczył 30 KB, Viber po cichu odrzuca takie wiadomości. Proszę sprawdzać
strlen(json_encode($data))przed wysłaniem. Druga: w odpowiedzi naset_webhooknie było"status":0, webhook nie jest ustawiony, bot nie otrzymuje wiadomości. Trzecia: używany jest stary format zauth_tokenw treści JSON. Od API 7.0 jest to ignorowane, wymagany jest nagłówekX-Viber-Auth-Token. W celu debugowania proszę włączyć logowanie odpowiedzi Viber API: zwraca on czytelny JSON z kodem błędu invalidAuthToken, badData lub missingData.
Czy można zmienić klawiaturę po wysłaniu?
Tak. Z każdą nową wiadomością można wysłać inną klawiaturę. Klient Viber zawsze pokazuje ostatnią otrzymaną. Pozwala to budować wielopoziomowe menu: menu główne → podmenu sekcji → konkretna treść. Każdy poziom z własnym zestawem przycisków, a użytkownik przemieszcza się między nimi, naciskając klawisze reply.
Ile kosztuje uruchomienie bota Viber w 2026 roku?
Od 5 lutego 2024 roku utworzenie nowego bota to 100 € miesięcznie za pośrednictwem oficjalnych partnerów Rakuten Viber. Boty utworzone przed tą datą działają na starych warunkach. W koszt wchodzi dedykowane konto z panelem administratora, analityka oraz dostęp do wszystkich typów wiadomości, w tym rich_media i Viber Pay. Część techniczna API dla botów komercyjnych i starych bezpłatnych jest identyczna.
Bot gotowy: co dalej
Menu klawiaturowe to tylko punkt wejścia. Gdy podstawowa mechanika jest już opanowana, proszę dodawać karuzele treści przez rich_media, płatności przez Viber Pay i segmentację użytkowników według tego, które przyciski naciskają najczęściej. Każdy nowy poziom nie zmienia podstawy: nadal czyta Pan/Pani event, nadal wysyła send_message z nagłówkiem X-Viber-Auth-Token.
Jeśli potrzebny jest działający przykład kodu, proszę zajrzeć do repozytorium viber-bot-php na GitHub, gdzie rozpisane są wszystkie zdarzenia, podpisywanie żądań i tworzenie klawiatury z obrazkami. A na wizualny start, oto półgodzinny tutorial z tworzenia bota Viber od podstaw:
Najważniejsze do zapamiętania: Viber Bot API jest prosty na starcie, ale wymagający co do szczegółów. Prawidłowy nagłówek uwierzytelniania, zaufany SSL, poprawny JSON do 30 KB i przejrzysty routing ActionBody, to wystarczy, aby bot działał bez zakłóceń. Proszę wziąć snippety powyżej, podstawić swój token i domenę i uruchomić pierwsze menu już dziś.



