
🤖 Chatbot Viber en PHP : envoi de messages structurés avec menus clavier
L’utilisateur appuie sur un bouton dans le chat et reçoit non pas du texte brut, mais une grille d’images, de liens et de touches colorées. Cela ressemble à une application native. En réalité, il s’agit simplement de JSON que votre script PHP renvoie via l’API REST de Viber.
Le problème avec la plupart des tutoriels: ils vous apprennent à répondre du texte à du texte. Un vrai bot professionnel doit afficher un menu sous forme de clavier avec des boutons de lien, des images et des carrousels. Sinon, l’utilisateur ne comprend tout simplement pas ce que le bot peut faire et s’en va. Depuis février 2024, Viber a rendu la création de bots payante (100 € par mois via les partenaires officiels Rakuten Viber), et l’authentification passe désormais par l’en-tête HTTP X-Viber-Auth-Token et non plus dans le corps de la requête. Les extraits de code avec auth_token dans le JSON ne fonctionnent plus.
Voici un bot Viber fonctionnel en PHP de A à Z: de la configuration du webhook à l’envoi d’un menu structuré avec des boutons de réponse et d’ouverture de lien. Le code est à jour pour la version 7.3 de l’API.
💡 Aperçu rapide:
- Enregistrez un bot commercial auprès d’un partenaire Viber et obtenez un jeton d’authentification
- Configurez le webhook via
set_webhookavec l’en-têteX-Viber-Auth-Token - Recevez les événements de rappel de Viber: webhook → subscribed → message
- Construisez un menu clavier (une grille de boutons avec couleurs, tailles et actions personnalisées)
- Envoyez une réponse structurée: texte, images, liens, carrousels via
send_message
Fonctionnement de l’API Viber Bot
L’API Viber Bot fonctionne sur un modèle REST: votre serveur reçoit des requêtes de rappel sur le webhook et répond en appelant https://chatapi.viber.com/pa/send_message. Chaque requête est authentifiée par un jeton dans l’en-tête X-Viber-Auth-Token.
Le cycle de base est le suivant:
- Vous enregistrez un bot via un partenaire Viber et recevez un jeton dans le panneau «Edit Info».
- Vous configurez un webhook (l’URL de votre serveur avec un certificat SSL valide; Let’s Encrypt fonctionne car il figure dans la liste de certificats Java de confiance de Viber; les certificats auto-signés ne sont pas acceptés).
- Viber envoie des requêtes POST à cette URL à chaque événement: un utilisateur s’est abonné, a envoyé un message, a appuyé sur un bouton.
- Votre script PHP lit le JSON entrant, analyse le champ
eventet répond en appelantsend_message.
La première étape pour comprendre est d’observer un bot en direct. Ouvrez Viber et recherchez le compte public d’une marque connue. Presque toutes en ont un avec un chatbot et un menu. La capture d’écran ci-dessous montre un résultat de recherche de compte public typique.

Après vous être abonné, vous entrez dans une conversation individuelle. Cela ressemble à peu près à ceci: un avatar, un message de bienvenue et un bouton pour démarrer la conversation.

Appuyez sur l’icône de message en haut à droite et envoyez «Bonjour». Si le bot est configuré avec un menu clavier, vous verrez une réponse avec une grille de boutons:

Il s’agit d’un message structuré. Il existe deux types de boutons: reply renvoie du texte au bot (en appuyant sur «Actualités» ou «Articles»), tandis que open-url ouvre un lien dans le navigateur. Écrivons maintenant le code qui produit cela.
Étape 1: Obtenir le jeton et configurer le webhook
Le jeton se trouve dans le panneau d’administration Viber: section Edit Info → champ App Key. Il s’agit d’une chaîne de lettres et de chiffres que vous incluez dans chaque requête API.
Le webhook se configure avec une seule requête POST vers https://chatapi.viber.com/pa/set_webhook. Le corps de la requête est du JSON contenant l’URL de votre gestionnaire et une liste des événements auxquels vous vous abonnez. L’en-tête est X-Viber-Auth-Token avec votre jeton.
Script de configuration du webhook (enregistrez-le sous setup.php et exécutez-le une fois):
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 }
Ce qui se passe ici: cURL envoie un POST à set_webhook. Le jeton est strictement dans l’en-tête, pas dans le corps. Viber renvoie {"status":0} en cas de succès et envoie immédiatement un rappel webhook à l’URL spécifiée pour vérifier sa disponibilité. Il est obligatoire de répondre {"status":0,"status_message":"ok"} à ce rappel (nous verrons cela à l’étape suivante).
Si vous recevez invalidUrl, vérifiez votre certificat SSL. Let’s Encrypt fonctionne; un certificat auto-signé non. Sur votre serveur, il suffit d’exécuter certbot --nginx -d your-domain.com et de configurer le renouvellement automatique via cron.
Étape 2: Recevoir les événements de rappel de Viber
Lorsqu’un utilisateur interagit avec le bot, Viber envoie une requête POST avec du JSON à votre webhook. Votre tâche consiste à lire le champ event et à réagir.
Un gestionnaire minimal webhook.php qui répond correctement à tous les événements:
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 }
Détail des événements:
webhookarrive une fois lors de la configuration du webhook. Vous devez renvoyer{"status":0,"status_message":"ok"}avec la liste des événements supportés. Sans cela, Viber ne confirmera pas l'URL et le bot ne démarrera pas.subscribedsignifie qu'un utilisateur s'est abonné au bot. C'est le moment idéal pour envoyer un menu de bienvenue.conversation_startedsignifie qu'un utilisateur a ouvert la conversation (pour la première fois ou à nouveau). Convient également pour afficher le menu principal.messageest l'événement de travail principal. Le champtextcontient soit un message libre de l'utilisateur, soit leActionBodyd'un bouton pressé (si son type estreply). C'est ainsi que le bot comprend ce qui a été sélectionné: «Actualités», «Articles» ou «Galerie».
La structure match() est disponible à partir de PHP 8. Si vous utilisez PHP 7.4, remplacez-la par switch.
Étape 3: Construire le menu du clavier
Un clavier est attaché à tout message via le champ keyboard dans l'objet JSON send_message. Il s'agit d'un tableau de boutons avec des paramètres distincts pour la largeur (Columns, 1-6), la hauteur (Rows, 1-2), la couleur de fond, le texte et l'action.
Une fonction qui assemble un menu principal à partir de sept boutons de réponse et d'un bouton de lien:
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 }
Les boutons avec Columns=2 et Rows=2 sont carrés, trois par ligne (2+2+2=6). Le bouton de lien avec Columns=4 occupe sa propre ligne. La grille est assemblée de gauche à droite, de haut en bas. La somme des Columns dans une ligne détermine la disposition.
Paramètres des boutons:
Paramètre | Valeurs | Rôle |
|---|---|---|
| 1-6 | Largeur du bouton en colonnes conditionnelles |
| 1-2 | Hauteur du bouton en lignes |
|
|
|
| chaîne | Pour |
| chaîne | Libellé sur le bouton, supporte les balises HTML b, i et la couleur de police |
| HEX | Couleur de fond du bouton |
| URL | Image au-dessus du bouton, JPEG, optionnelle |
Étape 4: Envoyer des données via send_message
Une fonction d'envoi que vous appelez depuis le gestionnaire d'événements:
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 }
La différence clé par rapport aux anciens tutoriels: le token est dans l'en-tête, pas dans le corps JSON. Le corps contient uniquement les champs receiver, type, text, keyboard, et optionnellement sender.name, sender.avatar, tracking_data. La taille maximale du JSON est de 30 Ko. Si vous la dépassez, Viber ignore le message silencieusement; vérifiez strlen(json_encode($data)) avant l'envoi.
Étape 5: Assembler le contenu des boutons
Lorsqu'un utilisateur appuie sur un bouton de réponse, son ActionBody est envoyé au webhook comme texte du message. Vous l'analysez dans match() et appelez la fonction appropriée. Voici un exemple pour une liste d'actualités avec images:
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 }
Du côté de l'utilisateur, cela ressemble à une grille de cartes: image à gauche, titre à droite. Appuyer sur n'importe quelle partie ouvre le lien dans le navigateur.

Une galerie, une liste d'articles ou des sondages sont assemblés exactement de la même manière; seuls le contenu du tableau $buttons et le texte du message changent. S'il y a beaucoup de contenu et que le JSON approche les 30 Ko, divisez-le en pages avec un bouton «Suivant».
Si vous préférez travailler avec une bibliothèque PHP prête à l'emploi plutôt qu'avec l'API brute, jetez un œil à viber-bot-php par Bogdaan. Elle gère pour vous la validation de signature, le routage des événements et la génération du JSON.
Résumé: séquence complète de lancement
- Obtenez le token dans le panneau d'administration Viber: section Edit Info → App Key.
- Déployez le gestionnaire sur un serveur avec HTTPS. Let's Encrypt fonctionne; un certificat auto-signé non. Viber valide les certificats par rapport à la liste de confiance des autorités de certification racine Java.
- Définissez le webhook avec une requête POST vers
https://chatapi.viber.com/pa/set_webhookavec le corps{"url":"https://your-domain/webhook.php","event_types":[...]}et l'en-têteX-Viber-Auth-Token. - Vérifiez la réponse:
{"status":0}signifie que c'est réussi. Viber enverra immédiatement un callbackwebhookà votre URL. Assurez-vous que le script renvoie{"status":0,"status_message":"ok"}. - Envoyez le premier menu: lors de l'événement
conversation_started, appelezgetMainMenu()et passez le résultat àsendToViber().
⁉️🤔 Foire aux questions
Puis-je utiliser un certificat SSL auto-signé pour le webhook?
Non. Viber valide le certificat par rapport à la liste des autorités de certification racine Java de confiance. Un certificat auto-signé provoque une erreur
invalidUrllors de la définition du webhook. Let's Encrypt figure dans cette liste et est gratuit. Sur votre serveur, exécutezcertbot --nginx -d your-domain.com, ajoutez le renouvellement automatique dans cron, et Viber acceptera le certificat sans problème.
Quelle est la différence entre reply et open-url dans ActionType?
replyrenvoieActionBodyau webhook comme texte de message. Le script PHP le voit dans$input['message']['text']et le route viamatch(). Utilisez ceci pour la navigation au sein du bot.open-urlouvre un lien dans un navigateur externe et ne déclenche pas le webhook. En pratique, les menus combinent les deux types: cinq ou six boutons reply pour les sections et un bouton large open-url pour naviguer vers le site web.
Quels types de messages l'API Viber Bot prend-elle en charge?
L'API Viber Bot prend en charge neuf valeurs
typedanssend_message:text(texte brut),picture(image JPEG),video(fichier vidéo),file(tout fichier jusqu'à 50 Mo),location(point géographique),contact(fiche contact),sticker(autocollant),rich_media(carrousel de cartes) eturl(aperçu de lien). Pour la liste actuelle avec les champs obligatoires, consultez la documentation du Viber Developers Hub. Le scénario le plus courant esttext+keyboardpour les menus etpicturepour l'envoi d'images. Les carrousels sont excellents pour les vitrines et les catalogues, mais nécessitent plus de code.
Que dois-je faire si l'utilisateur ne voit pas le clavier?
Il y a trois causes fréquentes. Premièrement: le JSON avec le clavier a dépassé 30 Ko, et Viber écarte silencieusement ces messages. Vérifiez
strlen(json_encode($data))avant l'envoi. Deuxièmement: la réponse àset_webhookne contenait pas"status":0, le webhook n'a pas été défini et le bot ne reçoit pas de messages. Troisièmement: vous utilisez l'ancien format avecauth_tokendans le corps JSON. Depuis l'API 7.0, cela est ignoré; vous avez besoin de l'en-têteX-Viber-Auth-Token. Pour le débogage, activez la journalisation de la réponse de l'API Viber: elle renvoie du JSON lisible avec les codes d'erreur invalidAuthToken, badData ou missingData.
Puis-je changer le clavier après l'envoi?
Oui. Avec chaque nouveau message, vous pouvez envoyer un clavier différent. Le client Viber affiche toujours le dernier reçu. Cela vous permet de construire des menus à plusieurs niveaux: menu principal → sous-menu de section → contenu spécifique. Chaque niveau a son propre ensemble de boutons, et l'utilisateur navigue entre eux en appuyant sur les touches reply.
Combien coûte le lancement d'un bot Viber en 2026?
Depuis le 5 février 2024, la création d'un nouveau bot coûte 100 € par mois via les partenaires officiels Rakuten Viber. Les bots créés avant cette date continuent sous les anciennes conditions. Le coût inclut un compte dédié avec un panneau d'administration, des analyses et l'accès à tous les types de messages, y compris rich_media et Viber Pay. La partie technique de l'API est identique pour les bots commerciaux et les anciens bots gratuits.
Le bot est prêt: la suite
Un menu clavier n'est qu'un point d'entrée. Une fois les mécanismes de base opérationnels, ajoutez des carrousels de contenu via rich_media, des paiements via Viber Pay et une segmentation des utilisateurs basée sur les boutons sur lesquels ils appuient le plus souvent. Chaque nouveau niveau ne change pas la base: vous lisez toujours event et vous envoyez toujours send_message avec l'en-tête X-Viber-Auth-Token.
Si vous avez besoin d'un exemple concret de code fonctionnel, consultez le dépôt viber-bot-php sur GitHub. Il couvre tous les événements, la signature des requêtes et la construction d'un clavier avec des images. Et pour un démarrage visuel, voici un tutoriel d'une demi-heure sur la création d'un bot Viber à partir de zéro:
La principale chose à retenir: l'API Viber Bot est simple au départ, mais exigeante sur les détails. Un en-tête d'authentification correct, un SSL de confiance, un JSON valide de moins de 30 Ko et un routage clair des ActionBody suffisent pour que le bot fonctionne sans échec. Prenez les extraits ci-dessus, substituez votre token et votre domaine, et lancez votre premier menu dès aujourd'hui.



