
🤖 Chatbot de Viber en PHP: envío de mensajes estructurados con menús de teclado
El usuario pulsa un botón en el chat y no recibe texto simple, sino una cuadrícula de imágenes, enlaces y teclas de colores. Parece una aplicación nativa. En realidad, es JSON plano que su script PHP devuelve a través de la API REST de Viber.
El problema con la mayoría de los tutoriales: enseñan a responder texto con texto. Un bot de negocio real debe mostrar un menú de teclado con botones de enlace, imágenes y carruseles. De lo contrario, el usuario simplemente no entenderá qué puede hacer el bot y se irá. Desde febrero de 2024, Viber ha puesto la creación de bots sobre una base comercial (100 € al mes a través de socios oficiales de Rakuten Viber), y la autenticación ahora va en la cabecera HTTP X-Viber-Auth-Token en lugar de en el cuerpo de la solicitud. Los fragmentos con auth_token dentro del JSON ya no funcionan.
Aquí tiene un bot de Viber funcional en PHP desde cero: desde la configuración del webhook hasta el envío de un menú de teclado estructurado con botones de respuesta y de apertura de URL. El código está actualizado para la versión 7.3 de la API.
💡 Resumen rápido:
- Registre un bot comercial a través de un socio de Viber y obtenga un token de autenticación
- Configure el webhook mediante
set_webhookcon la cabeceraX-Viber-Auth-Token - Reciba eventos de callback de Viber: webhook → subscribed → message
- Construya un menú de teclado (una cuadrícula de botones con colores, tamaños y acciones personalizados)
- Envíe una respuesta estructurada: texto, imágenes, enlaces, carruseles mediante
send_message
Cómo funciona la API de Viber Bot
La API de Viber Bot opera sobre un modelo REST: su servidor recibe solicitudes de callback en el webhook y responde llamando a https://chatapi.viber.com/pa/send_message. Cada solicitud se autentica con un token en la cabecera X-Viber-Auth-Token.
El ciclo básico es el siguiente:
- Usted registra un bot a través de un socio de Viber y recibe un token en el panel "Edit Info".
- Configura un webhook (la URL de su servidor con un SSL válido; Let's Encrypt funciona ya que está en la lista de certificados Java de confianza de Viber; los certificados autofirmados no se aceptan).
- Viber envía solicitudes POST a esta URL ante cada evento: un usuario se suscribió, envió un mensaje, pulsó un botón.
- Su script PHP lee el JSON entrante, analiza el campo
eventy responde llamando asend_message.
El primer paso para entenderlo es observar un bot en vivo. Abra Viber y busque una cuenta pública de cualquier marca conocida. Casi todas tienen un chatbot con un menú. La captura de pantalla siguiente muestra un resultado típico de búsqueda de cuenta pública.

Después de suscribirse, usted entra en un chat uno a uno. Se ve más o menos así: un avatar, un mensaje de bienvenida y un botón para iniciar la conversación.

Toque el icono de mensaje en la esquina superior derecha y envíe "Hola". Si el bot está configurado con un menú de teclado, verá una respuesta con una cuadrícula de botones:

Este es un mensaje estructurado. Hay dos tipos de botones: reply envía texto de vuelta al bot (al pulsar "Noticias" o "Artículos"), mientras que open-url abre un enlace en el navegador. Ahora escribamos el código que produce esto.
Paso 1: Obtener el token y configurar el webhook
El token se encuentra en el panel de administración de Viber: sección Edit Info → campo App Key. Es una cadena de letras y números que usted incluye en cada solicitud a la API.
El webhook se establece con una única solicitud POST a https://chatapi.viber.com/pa/set_webhook. El cuerpo de la solicitud es JSON con la URL de su manejador y una lista de eventos a los que se suscribe. La cabecera es X-Viber-Auth-Token con su token.
Script de configuración del webhook (guárdelo como setup.php y ejecútelo una vez):
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 }
Lo que sucede aquí: cURL envía un POST a set_webhook. El token va estrictamente en la cabecera, no en el cuerpo. Viber devuelve {"status":0} en caso de éxito e inmediatamente envía un callback webhook a la URL especificada para verificar su disponibilidad. Responder con {"status":0,"status_message":"ok"} a este callback es obligatorio (lo cubriremos en el siguiente paso).
Si recibe invalidUrl, verifique su certificado SSL. Let's Encrypt funciona; uno autofirmado no. En su servidor basta con ejecutar certbot --nginx -d your-domain.com y configurar la renovación automática mediante cron.
Paso 2: Recibir eventos de callback de Viber
Cuando un usuario interactúa con el bot, Viber envía una solicitud POST con JSON a su webhook. Su tarea es leer el campo event y reaccionar.
Un manejador mínimo webhook.php que responde correctamente a todos los eventos:
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 }
Desglose de eventos:
webhookllega una vez al configurar el webhook. Debe devolver{"status":0,"status_message":"ok"}con una lista de eventos soportados. Sin esto, Viber no confirmará la URL y el bot no arrancará.subscribedsignifica que un usuario se ha suscrito al bot. Es el momento ideal para enviar un menú de bienvenida.conversation_startedsignifica que un usuario abrió el chat (por primera vez o de nuevo). También es adecuado para mostrar el menú principal.messagees el evento principal de trabajo. El campotextcontiene un mensaje arbitrario del usuario o elActionBodyde un botón presionado (si su tipo esreply). Así entiende el bot qué se seleccionó: «News», «Articles» o «Gallery».
La construcción match() está disponible desde PHP 8. Si tiene PHP 7.4, sustitúyala por switch.
Step 3: Construir el menú del teclado
Un teclado se adjunta a cualquier mensaje mediante el campo keyboard en el objeto JSON de send_message. Es un arreglo de botones con configuraciones separadas para ancho (Columns, 1-6), alto (Rows, 1-2), color de fondo, texto y acción.
Una función que ensambla un menú principal a partir de siete botones de respuesta y un botón de enlace:
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 }
Los botones con Columns=2 y Rows=2 son cuadrados, tres por fila (2+2+2=6). El botón de enlace con Columns=4 ocupa su propia fila. La cuadrícula se ensambla de izquierda a derecha, de arriba abajo. La suma de Columns en una fila determina la disposición.
Parámetros de los botones:
Parámetro | Valores | Propósito |
|---|---|---|
| 1-6 | Ancho del botón en columnas condicionales |
| 1-2 | Alto del botón en filas |
|
|
|
| string | Para |
| string | Etiqueta en el botón, admite etiquetas HTML b, i y color de fuente |
| HEX | Color de fondo del botón |
| URL | Imagen sobre el botón, JPEG, opcional |
Step 4: Enviar datos mediante send_message
Una función de envío que se llama desde el manejador de eventos:
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 diferencia clave respecto a tutoriales antiguos: el token va en el encabezado, no en el cuerpo JSON. El cuerpo contiene solo los campos receiver, type, text, keyboard y, opcionalmente, sender.name, sender.avatar, tracking_data. El tamaño máximo del JSON es 30 KB. Si lo excede, Viber descarta el mensaje silenciosamente; verifique strlen(json_encode($data)) antes de enviar.
Step 5: Ensamblar contenido para los botones
Cuando un usuario presiona un botón de respuesta, su ActionBody se envía al webhook como texto del mensaje. Se analiza en match() y se llama a la función correspondiente. Aquí tiene un ejemplo para una lista de noticias con imágenes:
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 }
Del lado del usuario esto se ve como una cuadrícula de tarjetas: imagen a la izquierda, título a la derecha. Tocar cualquier parte abre el enlace en el navegador.

Una galería, lista de artículos o encuestas se ensamblan exactamente igual; solo cambian los contenidos del arreglo $buttons y el texto del mensaje. Si hay mucho contenido y el JSON se acerca a los 30 KB, divídalo en páginas con un botón «Next».
Si prefiere trabajar con una librería PHP ya lista en lugar de la API cruda, consulte viber-bot-php de Bogdaan. Maneja por usted la validación de firma, el enrutamiento de eventos y la generación de JSON.
Resumen: secuencia completa de lanzamiento
- Obtenga el token en el Panel de Administración de Viber: sección Editar Información → App Key.
- Despliegue el manejador en un servidor con HTTPS. Let's Encrypt funciona; un certificado autofirmado no. Viber valida los certificados contra la lista de confianza de Autoridades Raíz de Java.
- Configure el webhook con una solicitud POST a
https://chatapi.viber.com/pa/set_webhookcon el cuerpo{"url":"https://your-domain/webhook.php","event_types":[...]}y el encabezadoX-Viber-Auth-Token. - Verifique la respuesta:
{"status":0}significa éxito. Viber enviará inmediatamente una devolución de llamadawebhooka su URL. Asegúrese de que el script devuelva{"status":0,"status_message":"ok"}. - Envíe el primer menú: en el evento
conversation_startedllame agetMainMenu()y pase el resultado asendToViber().
⁉️🤔 Preguntas frecuentes
¿Puedo usar un certificado SSL autofirmado para el webhook?
No. Viber valida el certificado contra la lista de autoridades de certificación raíz de Java. Un certificado autofirmado provoca un error
invalidUrlal configurar el webhook. Let's Encrypt está en esa lista y es gratuito. En su servidor, ejecutecertbot --nginx -d your-domain.com, añada la renovación automática al cron y Viber aceptará el certificado sin problemas.
¿Cuál es la diferencia entre reply y open-url en ActionType?
replyenvíaActionBodyde vuelta al webhook como texto del mensaje. El script PHP lo recibe en$input['message']['text']y lo enruta mediantematch(). Úselo para la navegación dentro del bot.open-urlabre un enlace en un navegador externo y no activa el webhook. En la práctica, los menús combinan ambos tipos: cinco o seis botones de respuesta para las secciones y un botón ancho de open-url para navegar al sitio web.
¿Qué tipos de mensaje admite la API de Viber Bot?
La API de Viber Bot admite nueve valores de
typeensend_message:text(texto plano),picture(imagen JPEG),video(archivo de video),file(cualquier archivo de hasta 50 MB),location(punto geográfico),contact(tarjeta de contacto),sticker(sticker),rich_media(carrusel de tarjetas) yurl(vista previa de enlace). Para consultar la lista actual con los campos obligatorios, vea la documentación del Centro de Desarrolladores de Viber. El escenario más común estext+keyboardpara menús ypicturepara enviar imágenes. Los carruseles son excelentes para escaparates y catálogos, pero requieren más código.
¿Qué debo hacer si el usuario no ve el teclado?
Hay tres causas comunes. Primera: el JSON con el teclado superó los 30 KB y Viber descarta silenciosamente esos mensajes. Verifique
strlen(json_encode($data))antes de enviar. Segunda: la respuesta aset_webhookno contenía"status":0, el webhook no se configuró y el bot no está recibiendo mensajes. Tercera: está usando el formato antiguo conauth_tokenen el cuerpo JSON. Desde la API 7.0 esto se ignora; necesita el encabezadoX-Viber-Auth-Token. Para depurar, active el registro de la respuesta de la API de Viber: devuelve JSON legible con códigos de error como invalidAuthToken, badData o missingData.
¿Puedo cambiar el teclado después de enviarlo?
Sí. Con cada nuevo mensaje puede enviar un teclado diferente. El cliente de Viber siempre muestra el último recibido. Esto le permite construir menús de varios niveles: menú principal → submenú de sección → contenido específico. Cada nivel tiene su propio conjunto de botones y el usuario se mueve entre ellos presionando las teclas de respuesta.
¿Cuánto cuesta lanzar un bot de Viber en 2026?
Desde el 5 de febrero de 2024, crear un bot nuevo cuesta 100 € al mes a través de los socios oficiales de Rakuten Viber. Los bots creados antes de esa fecha continúan bajo los términos anteriores. El costo incluye una cuenta dedicada con panel de administración, analíticas y acceso a todos los tipos de mensaje, incluidos rich_media y Viber Pay. La parte técnica de la API es idéntica para los bots comerciales y los heredados gratuitos.
El bot está listo: lo que viene después
Un menú de teclado es solo un punto de entrada. Una vez que la mecánica básica funciona, añada carruseles de contenido mediante rich_media, pagos a través de Viber Pay y segmentación de usuarios según los botones que presionan con más frecuencia. Cada nuevo nivel no cambia la base: usted sigue leyendo event y sigue enviando send_message con el encabezado X-Viber-Auth-Token.
Si necesita un ejemplo en vivo de código funcional, consulte el repositorio viber-bot-php en GitHub. Cubre todos los eventos, la firma de solicitudes y la construcción de un teclado con imágenes. Y para un inicio visual, aquí tiene un tutorial de media hora sobre cómo crear un bot de Viber desde cero:
Lo principal para recordar: la API de Viber Bot es simple al inicio pero exigente con los detalles. Un encabezado de autenticación correcto, SSL de confianza, JSON válido por debajo de 30 KB y un enrutamiento claro de ActionBody son suficientes para mantener el bot funcionando sin fallos. Tome los fragmentos anteriores, sustituya su token y dominio, y lance su primer menú hoy.



