Raphael Serafim· Publicado em 28 de setembro de 2026· 10 min de leitura

Como criar um bot de WhatsApp com a API não oficial (do zero ao menu com botões)

Crie um bot de WhatsApp com a API não oficial: webhook em Node.js, máquina de estados, menu com lista e botões, 'digitando...' e passagem para humano.

Ver como Markdown

Para criar um bot de WhatsApp com a API não oficial, você conecta o número a uma instância da WAME por QR Code, aponta o webhook para o seu servidor e, a cada mensagem recebida, decide a resposta com uma máquina de estados — enviando texto, listas e botões pelos endpoints /message/text, /message/list e /message/button_reply. Sem cadastro de app na Meta, sem template pré-aprovado e sem janela de 24 horas: o bot responde com texto livre, na hora.

Este guia monta um bot de atendimento completo em Node.js, do zero: recebe a mensagem, mostra um menu, segue o fluxo de acordo com a escolha, simula "digitando..." e passa para um humano quando precisa. É um bot de regras — previsível, barato e suficiente para a maior parte do atendimento repetitivo. Se você quer respostas geradas por IA, o chatbot com a API da OpenAI parte da mesma base.

Por que a API não oficial é boa para bots

  • Começa em minutos. Crie a instância, leia o QR Code em Aparelhos conectados e o número já está pronto. Prefere não usar QR? Há o código de pareamento.
  • Texto livre sempre. Nada de template aprovado para iniciar ou retomar conversa — detalhes em API do WhatsApp sem template.
  • Recursos ricos: listas, botões, enquetes, reações, figurinhas, grupos. Veja todos em recursos da API não oficial.
  • Hospedada e gerenciada. Diferente de rodar Baileys no seu servidor, a WAME cuida da sessão, da reconexão e da infraestrutura — 99,9% de uptime, suporte 24/7 em português, plataforma no ar desde 2017.

Uma observação honesta: a camada não oficial não é afiliada nem endossada pelo WhatsApp ou pela Meta, e o uso é de responsabilidade de quem opera o número.

Passo 1: conectar e configurar o webhook

Gere o QR Code da instância:

bash
curl -X POST "https://us.api-wa.me/SUA_KEY/instance"

Depois de escanear, configure para onde vão os eventos. Use o formato meta, que entrega tudo no envelope da Cloud API:

bash
curl -X PUT "https://us.api-wa.me/SUA_KEY/instance" \
  -H "Content-Type: application/json" \
  -d '{
    "allowWebhook": true,
    "allowNumber": "all",
    "webhookMessage": "https://seu-servidor.com/webhook/wame",
    "webhookFormat": "meta"
  }'

Em desenvolvimento, exponha a porta local com ngrok ou similar.

Passo 2: receber e extrair a mensagem

O evento chega assim:

json
{
  "object": "wame",
  "provider": "whatsapp",
  "entry": [{
    "changes": [{
      "field": "messages",
      "value": {
        "messages": [{
          "from": "5511999999999",
          "id": "wamid.XXXX",
          "type": "text",
          "text": { "body": "oi" }
        }]
      }
    }]
  }]
}

Um extrator com as guardas que evitam loop e erro:

javascript
function extrair(body) {
  const msg = body?.entry?.[0]?.changes?.[0]?.value?.messages?.[0];
  if (!msg) return null; // status de entrega também chega aqui

  // Texto digitado ou escolha de lista/botão.
  // Os campos da resposta interativa estão descritos em /docs.
  const texto = msg.text?.body ?? msg.interactive?.list_reply?.id
    ?? msg.interactive?.button_reply?.id;
  if (!texto) return null;

  return { de: msg.from, id: msg.id, texto: String(texto).trim() };
}

Passo 3: a máquina de estados

O erro clássico de bot é um amontoado de if que não sabe em que ponto da conversa o cliente está. A solução é guardar um estado por contato e ter uma função por estado:

javascript
const sessoes = new Map(); // em produção: Redis ou banco

const fluxo = {
  inicio: async (c) => {
    await enviarMenu(c.de);
    return 'menu';
  },

  menu: async (c) => {
    switch (c.texto) {
      case 'pedido':
        await enviarTexto(c.de, 'Me passa o número do pedido, por favor.');
        return 'aguardando_pedido';
      case 'boleto':
        await enviarTexto(c.de, 'Qual o CPF ou CNPJ do cadastro?');
        return 'aguardando_documento';
      case 'humano':
        await enviarTexto(c.de, 'Certo! Já chamo alguém da equipe. 🙋');
        await avisarEquipe(c.de);
        return 'humano';
      default:
        await enviarMenu(c.de);
        return 'menu';
    }
  },

  aguardando_pedido: async (c) => {
    const status = await consultarPedido(c.texto); // seu sistema
    await enviarTexto(c.de, status ?? 'Não achei esse pedido. Confere o número?');
    return status ? 'inicio' : 'aguardando_pedido';
  },

  aguardando_documento: async (c) => {
    const link = await gerarSegundaVia(c.texto); // seu sistema
    await enviarTexto(c.de, link ? `Aqui está: ${link}` : 'Não encontrei esse documento.');
    return 'inicio';
  },

  humano: async () => 'humano', // bot em silêncio até a equipe liberar
};

Cada estado recebe a mensagem, faz o que precisa e devolve o próximo estado. Adicionar um fluxo novo é adicionar uma função, sem mexer nas outras.

Passo 4: o menu com lista

Para mais de três opções, a lista é melhor que botões:

javascript
const BASE = 'https://us.api-wa.me/SUA_KEY';

async function post(caminho, corpo) {
  const r = await fetch(`${BASE}${caminho}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(corpo),
  });
  if (!r.ok) throw new Error(`${caminho} ${r.status}`);
}

const enviarTexto = (to, text) => post('/message/text', { to, text });

const enviarMenu = (to) =>
  post('/message/list', {
    to,
    title: 'Atendimento',
    text: 'Como posso ajudar?',
    buttonText: 'Ver opções',
    footer: 'Responda a qualquer momento',
    sections: [{
      title: 'Opções',
      rows: [
        { title: 'Status do pedido', rowId: 'pedido' },
        { title: 'Segunda via de boleto', rowId: 'boleto' },
        { title: 'Falar com atendente', rowId: 'humano' },
      ],
    }],
  });

Para decisões curtas (sim/não, confirmar/cancelar), use botões de resposta rápida:

javascript
const confirmar = (to) =>
  post('/message/button_reply', {
    to,
    header: { title: 'Confirmação' },
    text: 'Posso agendar para amanhã às 10h?',
    buttons: [
      { type: 'quick_reply', id: 'sim', text: 'Pode sim' },
      { type: 'quick_reply', id: 'nao', text: 'Outro horário' },
    ],
  });

Os rowId e id são o que volta no webhook quando o cliente escolhe — por isso o estado menu compara com 'pedido', 'boleto' e 'humano'.

O detalhe da primeira mensagem

Um comportamento pouco conhecido: o WhatsApp do destinatário não mostra botões nem listas na primeira mensagem de uma conversa que nunca existiu. Se o bot inicia o contato mandando um menu, o cliente pode não ver nada. A solução é abrir com um texto antes do menu: uma mensagem normal já basta, e o menu seguinte chega certo. Em conversas iniciadas pelo cliente, como neste bot, a questão nem aparece.

Passo 5: parecer gente (digitando e lido)

Resposta em 0 milissegundos tem cara de robô — para o cliente e para o WhatsApp. Antes de responder, marque como lida e mostre "digitando...":

javascript
async function prepararResposta(to, messageId) {
  await post('/message/read', { messageId });
  await post('/message/presence', { to, status: 'composing' });
  await new Promise((r) => setTimeout(r, 1200));
}

A WAME já aplica um ritmo humano de leitura e digitação por trás de cada instância, proporcional ao tamanho do texto; o trecho acima é para quando você quer controlar isso explicitamente. Mais em como simular "digitando..." pela API.

Passo 6: juntar tudo no webhook

javascript
import express from 'express';
const app = express();
app.use(express.json());

app.post('/webhook/wame', async (req, res) => {
  res.sendStatus(200); // responda já; o processamento vem depois

  const c = extrair(req.body);
  if (!c) return;

  const estado = sessoes.get(c.de) ?? 'inicio';
  if (estado === 'humano') return; // atendente no comando

  try {
    await prepararResposta(c.de, c.id);
    const proximo = await fluxo[estado](c);
    sessoes.set(c.de, proximo);
  } catch (e) {
    console.error('falha no bot', c.de, e);
    await enviarTexto(c.de, 'Tive um problema aqui. Já chamo alguém da equipe.');
    sessoes.set(c.de, 'humano');
  }
});

app.listen(3000);

Quando o atendente terminar, seu painel volta o estado do contato para inicio e o bot reassume. Se vários atendentes dividem o mesmo número, veja multiatendimento no mesmo número.

Checklist antes de colocar no ar

  • Idempotência: o mesmo evento pode chegar duas vezes. Guarde os id já processados — veja webhook em produção.
  • Estado persistente: o Map em memória some no deploy. Use Redis ou banco.
  • Saída sempre visível: toda tela do bot precisa de um caminho para humano.
  • Palavra de saída: se alguém escrever "sair" ou "parar", respeite e registre — opt-out vale para bot também.
  • Tempo de sessão: se o cliente some por horas no meio de um fluxo, volte para inicio na próxima mensagem.

Bot e bloqueio: o que realmente importa

Um bot que responde a quem escreveu é o uso mais seguro possível da API não oficial — e é por isso que a taxa de bloqueio nesse cenário é muito baixa quando o número é usado do jeito certo. Além do comportamento, a WAME protege cada instância com identidade de dispositivo própria e coerente com o país do número, limites de ritmo, reconexão espaçada e um monitor de saúde que pausa envios ao primeiro sinal de problema.

Onde o bot vira risco é quando ele passa a iniciar conversas em massa com quem nunca pediu. Isso é spam, e a WAME não apoia. Se o seu projeto precisa de campanhas, comece pela lista com opt-in e pelo controle de ritmo.

Conclusão

Criar um bot de WhatsApp com a API não oficial é juntar quatro peças: conexão por QR Code, webhook no formato meta, uma máquina de estados com uma função por etapa e respostas com lista, botões e texto livre. Some a isso "digitando...", passagem para humano e idempotência, e o bot está pronto para produção — sem aprovação da Meta, sem template e sem cobrança por mensagem. Quando as perguntas ficarem abertas demais para um menu, é hora de plugar IA na mesma estrutura. Todos os endpoints estão na documentação.

Pronto para automatizar seu WhatsApp?

Crie sua conta gratuita e comece a enviar mensagens pela API em minutos.

Começar grátis

Perguntas frequentes

Como criar um bot de WhatsApp com a API não oficial?+

Conecte seu número a uma instância da WAME por QR Code ou código de pareamento, configure a URL do seu servidor como webhook e, a cada mensagem recebida, decida a resposta com uma máquina de estados. As respostas saem por endpoints como /message/text, /message/list e /message/button_reply.

Preciso de aprovação da Meta ou de templates para o bot?+

Não. Na API não oficial o bot envia texto livre, listas e botões sem cadastro de app na Meta e sem templates pré-aprovados. A camada não oficial não é afiliada ao WhatsApp e o uso é de responsabilidade de quem opera o número.

Por que meus botões não aparecem na primeira mensagem?+

O WhatsApp do destinatário não renderiza mensagens interativas (botões e listas) na primeira mensagem de uma conversa que nunca existiu. A solução é abrir a conversa com um texto antes do menu: uma mensagem normal já basta, e em conversas iniciadas pelo cliente o problema não aparece.

O bot precisa ser feito com inteligência artificial?+

Não. Um bot de regras com menu resolve a maior parte do atendimento repetitivo (segunda via, horário, status de pedido) de forma previsível e barata. IA entra quando as perguntas são abertas demais para um menu.

Como passo a conversa do bot para um atendente?+

Tenha um estado 'humano' na máquina de estados. Quando o cliente escolhe falar com alguém, o bot avisa, marca a conversa e para de responder aquele número até o atendente encerrar o atendimento.

Continue lendo