
🤖 Chatbot Viber em PHP: envio de mensagens estruturadas com menus de teclado
O utilizador prime um botão no chat e recebe, não texto simples, mas uma grelha de imagens, links e teclas coloridas. Parece uma aplicação nativa. Na realidade, é JSON simples que o seu script PHP devolve através da API REST do Viber.
O problema com a maioria dos tutoriais: ensinam a responder com texto a texto. Um bot de negócios real tem de apresentar um menu de teclado com botões de link, imagens e carrosséis. Caso contrário, o utilizador simplesmente não percebe o que o bot pode fazer e abandona. Desde fevereiro de 2024, o Viber passou a criação de bots para uma base comercial (100 € por mês através de parceiros oficiais Rakuten Viber), e a autenticação passou a ser feita no cabeçalho HTTP X-Viber-Auth-Token em vez de no corpo do pedido. Os trechos de código com auth_token dentro do JSON já não funcionam.
Aqui está um bot Viber funcional em PHP de raiz: desde a configuração do webhook até ao envio de um menu de teclado estruturado com botões de resposta e de abertura de URL. O código está atualizado para a versão 7.3 da API.
💡 Visão geral rápida:
- Registar um bot comercial através de um parceiro Viber e obter um token de autenticação
- Configurar o webhook via
set_webhookcom o cabeçalhoX-Viber-Auth-Token - Receber eventos de callback do Viber: webhook → subscribed → message
- Construir um menu de teclado (uma grelha de botões com cores, tamanhos e ações personalizados)
- Enviar uma resposta estruturada: texto, imagens, links, carrosséis via
send_message
Como funciona a API de Bots do Viber
A API de Bots do Viber funciona num modelo REST: o seu servidor recebe pedidos de callback no webhook e responde chamando https://chatapi.viber.com/pa/send_message. Cada pedido é autenticado com um token no cabeçalho X-Viber-Auth-Token.
O ciclo básico é o seguinte:
- Regista um bot através de um parceiro Viber e recebe um token no painel "Edit Info".
- Configura um webhook (o URL do seu servidor com um SSL válido; o Let's Encrypt funciona, uma vez que está na lista de certificados Java confiáveis do Viber; certificados autoassinados não são aceites).
- O Viber envia pedidos POST para este URL em cada evento: um utilizador subscreveu, enviou uma mensagem, pressionou um botão.
- O seu script PHP lê o JSON recebido, analisa o campo
evente responde chamandosend_message.
O primeiro passo para compreender é observar um bot ao vivo. Abra o Viber e procure a conta pública de qualquer marca conhecida. Quase todas têm um chatbot com um menu. A captura de ecrã abaixo mostra um resultado típico de pesquisa de uma conta pública.

Após subscrever, entra num chat individual. Tem um aspeto aproximado a este: um avatar, uma mensagem de boas-vindas e um botão para iniciar a conversa.

Toque no ícone de mensagem no canto superior direito e envie "Olá". Se o bot estiver configurado com um menu de teclado, verá uma resposta com uma grelha de botões:

Isto é uma mensagem estruturada. Existem dois tipos de botões: reply envia texto de volta para o bot (ao pressionar "Notícias" ou "Artigos"), enquanto open-url abre um link no navegador. Agora vamos escrever o código que produz isto.
Passo 1: Obter o token e configurar o webhook
O token está localizado no painel de administração do Viber: secção Edit Info → campo App Key. É uma cadeia de letras e números que inclui em cada pedido à API.
O webhook é definido com um único pedido POST para https://chatapi.viber.com/pa/set_webhook. O corpo do pedido é JSON com o URL do seu manipulador e uma lista de eventos a que subscreve. O cabeçalho é X-Viber-Auth-Token com o seu token.
Script de configuração do webhook (guarde como setup.php e execute uma 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 }
O que acontece aqui: o cURL envia um POST para set_webhook. O token está estritamente no cabeçalho, não no corpo. O Viber devolve {"status":0} em caso de sucesso e envia imediatamente um callback webhook para o URL especificado para verificar a sua disponibilidade. Responder com {"status":0,"status_message":"ok"} a este callback é obrigatório (vamos abordar isto no próximo passo).
Se receber invalidUrl, verifique o seu certificado SSL. O Let's Encrypt funciona; autoassinado não. No seu servidor, basta executar certbot --nginx -d your-domain.com e configurar a renovação automática via cron.
Passo 2: Receber eventos de callback do Viber
Quando um utilizador interage com o bot, o Viber envia um pedido POST com JSON para o seu webhook. A sua tarefa é ler o campo event e reagir.
Um manipulador mínimo webhook.php que responde corretamente a todos os 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 }
Desagregação dos eventos:
webhookchega uma vez, quando o webhook é configurado. Deve devolver{"status":0,"status_message":"ok"}com uma lista de eventos suportados. Sem isto, o Viber não confirma o URL e o bot não arranca.subscribedsignifica que um utilizador subscreveu o bot. É o momento ideal para enviar um menu de boas-vindas.conversation_startedsignifica que um utilizador abriu o chat (pela primeira vez ou novamente). Também adequado para mostrar o menu principal.messageé o evento principal de trabalho. O campotextcontém ou uma mensagem arbitrária do utilizador ou oActionBodyde um botão premido (se o seu tipo forreply). É assim que o bot percebe o que foi selecionado: «News», «Articles» ou «Gallery».
A construção match() está disponível a partir do PHP 8. Se tiver PHP 7.4, substitua-a por switch.
Passo 3: Construir o menu de teclado
Um teclado é anexado a qualquer mensagem através do campo keyboard no objeto JSON send_message. É um array de botões com definições separadas para largura (Columns, 1-6), altura (Rows, 1-2), cor de fundo, texto e ação.
Uma função que monta um menu principal a partir de sete botões de resposta e um botão de link:
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 }
Os botões com Columns=2 e Rows=2 são quadrados, três por linha (2+2+2=6). O botão de link com Columns=4 ocupa a sua própria linha. A grelha é montada da esquerda para a direita, de cima para baixo. A soma de Columns numa linha determina o layout.
Parâmetros dos botões:
Parâmetro | Valores | Finalidade |
|---|---|---|
| 1-6 | Largura do botão em colunas condicionais |
| 1-2 | Altura do botão em linhas |
|
|
|
| string | Para |
| string | Rótulo no botão, suporta as tags HTML b, i e cor de fonte |
| HEX | Cor de fundo do botão |
| URL | Imagem no topo do botão, JPEG, opcional |
Passo 4: Enviar dados via send_message
Uma função de envio que se chama a partir do gestor 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 }
A diferença fundamental em relação a tutoriais antigos: o token vai no cabeçalho, não no corpo do JSON. O corpo contém apenas os campos receiver, type, text, keyboard e, opcionalmente, sender.name, sender.avatar, tracking_data. O tamanho máximo do JSON é 30 KB. Se o exceder, o Viber descarta a mensagem silenciosamente; verifique strlen(json_encode($data)) antes de enviar.
Passo 5: Montar conteúdo para botões
Quando um utilizador prime um botão de resposta, o seu ActionBody é enviado para o webhook como texto da mensagem. Analisa-o no match() e chama a função apropriada. Eis um exemplo para uma lista de notícias com imagens:
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 }
Do lado do utilizador, isto parece uma grelha de cartões: imagem à esquerda, título à direita. Tocar em qualquer parte abre o link no navegador.

Uma galeria, lista de artigos ou sondagens são montadas exatamente da mesma forma; apenas mudam o conteúdo do array $buttons e o texto da mensagem. Se houver muito conteúdo e o JSON se aproximar dos 30 KB, divida-o em páginas com um botão «Seguinte».
Se preferir trabalhar com uma biblioteca PHP já pronta em vez da API pura, veja o viber-bot-php do Bogdaan. Trata da validação de assinatura, encaminhamento de eventos e geração de JSON por si.
Resumo: sequência completa de lançamento
- Obtenha o token no Painel de Administração do Viber: secção Edit Info → App Key.
- Implemente o handler num servidor com HTTPS. O Let's Encrypt funciona; um certificado autoassinado não. O Viber valida os certificados contra a lista de Autoridades de Certificação Raiz Java fidedignas.
- Configure o webhook com um pedido POST para
https://chatapi.viber.com/pa/set_webhookcom o corpo{"url":"https://your-domain/webhook.php","event_types":[...]}e o cabeçalhoX-Viber-Auth-Token. - Verifique a resposta:
{"status":0}significa sucesso. O Viber enviará de imediato um callbackwebhookpara o seu URL. Certifique-se de que o script devolve{"status":0,"status_message":"ok"}. - Envie o primeiro menu: no evento
conversation_startedchamegetMainMenu()e passe o resultado asendToViber().
⁉️🤔 Perguntas frequentes
Posso usar um certificado SSL autoassinado para o webhook?
Não. O Viber valida o certificado contra a lista de autoridades de certificação raiz Java fidedignas. Um certificado autoassinado provoca um erro
invalidUrlao configurar o webhook. O Let's Encrypt está nessa lista e é gratuito. No seu servidor, executecertbot --nginx -d your-domain.com, adicione a renovação automática ao cron e o Viber aceitará o certificado sem problemas.
Qual é a diferença entre reply e open-url no ActionType?
replyreenvia oActionBodypara o webhook como texto de mensagem. O script PHP vê-o em$input['message']['text']e encaminha-o através dematch(). Use isto para navegação dentro do bot.open-urlabre um link num navegador externo e não aciona o webhook. Na prática, os menus combinam ambos os tipos: cinco ou seis botões de reply para secções e um botão largo de open-url para navegar para o website.
Que tipos de mensagem suporta a Viber Bot API?
A Viber Bot API suporta nove valores de
typeemsend_message:text(texto simples),picture(imagem JPEG),video(ficheiro de vídeo),file(qualquer ficheiro até 50 MB),location(ponto geográfico),contact(cartão de contacto),sticker(sticker),rich_media(carrossel de cartões) eurl(pré-visualização de link). Para a lista atual com os campos obrigatórios, consulte a documentação do Viber Developers Hub. O cenário mais comum étext+keyboardpara menus epicturepara envio de imagens. Os carrosséis são ótimos para montras e catálogos, mas exigem mais código.
O que devo fazer se o utilizador não vir o teclado?
Há três causas comuns. Primeira: o JSON com o teclado excedeu 30 KB e o Viber descarta silenciosamente essas mensagens. Verifique
strlen(json_encode($data))antes de enviar. Segunda: a resposta aoset_webhooknão continha"status":0, o webhook não foi configurado e o bot não está a receber mensagens. Terceira: está a usar o formato antigo comauth_tokenno corpo do JSON. Desde a API 7.0 que isto é ignorado; precisa do cabeçalhoX-Viber-Auth-Token. Para depuração, ative o registo da resposta da API do Viber: ela devolve JSON legível com códigos de erro invalidAuthToken, badData ou missingData.
Posso alterar o teclado depois de enviar?
Sim. A cada nova mensagem pode enviar um teclado diferente. O cliente Viber mostra sempre o último recebido. Isto permite-lhe construir menus de vários níveis: menu principal → submenu de secção → conteúdo específico. Cada nível tem o seu próprio conjunto de botões e o utilizador move-se entre eles premindo as teclas de reply.
Quanto custa lançar um bot Viber em 2026?
Desde 5 de fevereiro de 2024, criar um novo bot custa 100 € por mês através dos parceiros oficiais da Rakuten Viber. Os bots criados antes dessa data continuam sob os termos antigos. O custo inclui uma conta dedicada com painel de administração, analítica e acesso a todos os tipos de mensagem, incluindo rich_media e Viber Pay. A parte técnica da API é idêntica para bots comerciais e bots gratuitos legados.
O bot está pronto: o que vem a seguir
Um menu de teclado é apenas um ponto de entrada. Quando a mecânica básica estiver a funcionar, adicione carrosséis de conteúdo via rich_media, pagamentos via Viber Pay e segmentação de utilizadores com base nos botões que pressionam com mais frequência. Cada novo nível não altera a base: continua a ler event e a enviar send_message com o cabeçalho X-Viber-Auth-Token.
Se precisar de um exemplo real de código funcional, consulte o repositório viber-bot-php no GitHub. Ele cobre todos os eventos, assinatura de pedidos e construção de um teclado com imagens. E para um início visual, aqui está um tutorial de meia hora sobre como criar um bot Viber de raiz:
O principal a reter: a Viber Bot API é simples no início, mas exigente nos detalhes. Um cabeçalho de autenticação correto, SSL fidedigno, JSON válido com menos de 30 KB e um encaminhamento claro de ActionBody são suficientes para manter o bot a funcionar sem falhas. Pegue nos excertos acima, substitua o seu token e domínio e lance o seu primeiro menu hoje.



