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

IA para atender ligação no WhatsApp pela API: como montar um agente de voz

Como montar um agente de voz que atende ligações no WhatsApp pela API: webhook, áudio em tempo real por Socket.IO, transcrição, IA, voz sintética e handoff.

Ver como Markdown

Para uma IA atender ligação no WhatsApp pela API, você junta quatro peças: o webhook avisa que alguém está ligando, POST /{key}/call/accept atende, o Socket.IO entrega a voz do cliente em tempo real (evento call-audio) e o seu código devolve a fala da IA pelo evento call-audio-in. Entre uma ponta e outra ficam a transcrição, o modelo de linguagem e a voz sintética. Com a API não oficial da WAME, tudo isso roda no número que você já usa, sem aprovação na Meta.

Este guia mostra a arquitetura completa e um esqueleto em Node.js que você pode adaptar. Os detalhes de latência e interrupção têm um artigo próprio: agente de voz no WhatsApp: latência, interrupção e silêncio.

A camada não oficial da WAME é independente e não é afiliada, endossada ou suportada pelo WhatsApp ou pela Meta. O uso é de responsabilidade de quem envia.

O que acontece numa ligação atendida por IA

Do toque ao "até logo", o fluxo é este:

  1. Alguém liga. O webhook da instância recebe um evento com field: "call" e a chamada em value.calls, com status: "offer".
  2. Você decide se atende. Horário, cliente conhecido, fila de atendimento: é aqui que você escolhe entre atender com IA, recusar e responder por mensagem ou deixar tocar.
  3. Você atende. POST /{key}/call/accept com o callId e o from_jid de quem ligou. A API atende já com o canal de áudio aberto.
  4. A voz do cliente chega pelo socket. Cada pedaço de áudio vem no evento call-audio, identificado pelo callId.
  5. Você entende e responde. Detecta quando a pessoa parou de falar, transcreve, pede a resposta ao modelo e sintetiza a voz.
  6. A IA fala. Você manda o áudio pelo evento call-audio-in, em tempo real, ou envia um arquivo pronto com POST /{key}/call/{callId}/audio.
  7. A ligação termina. POST /{key}/call/end, e o resumo vai para o seu sistema.

A diferença para um chatbot de texto é o tempo real: em vez de uma mensagem inteira, você recebe um fluxo contínuo de áudio e precisa decidir sozinho quando a pessoa terminou de falar.

Pré-requisitos

  • Uma instância não oficial conectada, com permissão de chamadas habilitada.
  • O webhook configurado no formato meta (via PUT /{key}/instance).
  • Um servidor Node.js com acesso público para o webhook.
  • Três provedores de IA, que podem ser do mesmo fornecedor ou não: transcrição (algo na linha do Whisper), modelo de linguagem e voz sintética com saída em streaming. Existe ainda a alternativa dos modelos que já recebem fala e devolvem fala (speech-to-speech), que substituem as três etapas por uma.

Este artigo não amarra você a um fornecedor. O código usa três funções, transcrever, responder e sintetizar, que você implementa com o serviço que escolher.

O formato do áudio

Os dois sentidos usam o mesmo formato: PCM de 16 bits, little-endian, mono, 16 kHz, em base64 no campo pcm.

Isso importa por dois motivos. Primeiro, muitos serviços de transcrição aceitam exatamente esse formato, então o áudio pode ir quase direto. Segundo, alguns provedores de voz sintética devolvem áudio a 24 kHz ou mais; nesse caso você precisa reamostrar para 16 kHz antes de enviar, ou a voz sai acelerada e aguda.

Para arquivos prontos (saudação gravada, aviso de horário), o caminho mais simples é POST /{key}/call/{callId}/audio com url ou base64: a API converte qualquer formato de áudio para o formato da chamada e coloca na fila.

Conectar o socket

O áudio da ligação não passa pelo webhook: ele é contínuo demais para isso. Ele chega por Socket.IO, e a conexão precisa da key da instância tanto em auth quanto em query — a query é o que leva a conexão até o servidor que cuida da sua instância.

javascript
import { io } from 'socket.io-client';

const KEY = process.env.WAME_KEY;

const socket = io('https://us.api-wa.me', {
  auth: { key: KEY },
  query: { key: KEY },
  transports: ['websocket', 'polling'],
});

socket.on('connect', () => console.log('socket conectado'));
socket.on('error', (e) => console.error('erro no socket', e));

O socket só recebe eventos da própria instância. Se você mandar áudio para uma chamada que já terminou ou que não foi atendida, o evento error volta com o código call_session_not_found.

O esqueleto do agente

O código abaixo junta webhook, socket e estado por chamada. As três funções de IA ficam como contratos para você preencher.

javascript
import express from 'express';
import { io } from 'socket.io-client';

const KEY = process.env.WAME_KEY;
const BASE = `https://us.api-wa.me/${KEY}`;

// ---- Contratos de IA: implemente com o provedor que escolher ----
async function transcrever(pcm16k) { /* Buffer PCM16 16 kHz -> texto */ }
async function responder(historico) { /* mensagens -> { texto, encerrar, humano } */ }
async function sintetizar(texto) { /* texto -> Buffer PCM16 16 kHz (reamostre se preciso) */ }

const post = (path, body) =>
  fetch(`${BASE}${path}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  }).then((r) => r.json());

// Estado por ligação
const chamadas = new Map();

const socket = io('https://us.api-wa.me', {
  auth: { key: KEY },
  query: { key: KEY },
  transports: ['websocket', 'polling'],
});

// Fala da IA: envia em pedaços pequenos (~40 ms) para poder parar no meio.
const BYTES_POR_PEDACO = 16000 * 2 * 0.04; // 16 kHz * 2 bytes * 40 ms
function falar(callId, pcm) {
  for (let i = 0; i < pcm.length; i += BYTES_POR_PEDACO) {
    const pedaco = pcm.subarray(i, i + BYTES_POR_PEDACO);
    socket.emit('call-audio-in', { callId, pcm: pedaco.toString('base64') });
  }
}

// Voz do cliente
socket.on('call-audio', ({ callId, pcm }) => {
  const c = chamadas.get(callId);
  if (!c) return;
  c.detector.alimentar(Buffer.from(pcm, 'base64'));
});

async function aoTerminarFala(callId, trecho) {
  const c = chamadas.get(callId);
  if (!c || c.ocupado) return;
  c.ocupado = true;
  try {
    const texto = await transcrever(trecho);
    if (!texto?.trim()) return;
    c.historico.push({ role: 'user', content: texto });

    const r = await responder(c.historico);
    c.historico.push({ role: 'assistant', content: r.texto });
    falar(callId, await sintetizar(r.texto));

    if (r.encerrar || r.humano) {
      setTimeout(() => encerrar(callId, r.humano), 4000); // deixa a despedida tocar
    }
  } finally {
    c.ocupado = false;
  }
}

async function encerrar(callId, precisaHumano) {
  const c = chamadas.get(callId);
  if (!c) return;
  await post('/call/end', { callId, peerJid: c.jid });
  chamadas.delete(callId);
  if (precisaHumano) await abrirTicket(c); // sua fila, seu CRM
}

const app = express();
app.use(express.json());

app.post('/webhook/wame', async (req, res) => {
  res.sendStatus(200); // responda já; o resto roda em segundo plano

  const change = req.body?.entry?.[0]?.changes?.[0];
  if (change?.field !== 'call') return;
  const ch = change.value?.calls?.[0];
  if (!ch || ch.status !== 'offer' || ch.is_group) return;

  if (!deveAtenderComIA(ch)) return; // horário, cliente, fila...

  const jid = ch.from_jid ?? ch.from;
  await post('/call/accept', { callId: ch.id, callFrom: jid });

  chamadas.set(ch.id, {
    jid,
    telefone: ch.from,
    historico: [],
    ocupado: false,
    detector: criarDetectorDeFala((trecho) => aoTerminarFala(ch.id, trecho)),
  });

  const saudacao =
    'Olá! Aqui é a assistente virtual da Loja Exemplo. ' +
    'Esta ligação é atendida por inteligência artificial e pode ser transcrita. ' +
    'Como posso ajudar?';
  falar(ch.id, await sintetizar(saudacao));
});

app.listen(3000);

Três pontos desse código merecem atenção:

  • from_jid, não from. O from vem só com dígitos; para atender e encerrar, a API precisa do JID completo, que chega em from_jid.
  • Um estado por callId. Várias ligações podem acontecer ao mesmo tempo, e o áudio de todas chega pelo mesmo socket. Tudo é indexado pelo callId.
  • O webhook responde 200 antes de atender. Webhook lento é reenviado; sem isso, você tentaria atender a mesma ligação duas vezes.

Detectar quando a pessoa terminou de falar

O criarDetectorDeFala é a peça que transforma um fluxo contínuo em frases. A versão mais simples mede a energia de cada pedaço de áudio e considera que a pessoa terminou quando há silêncio por algumas centenas de milissegundos:

javascript
function criarDetectorDeFala(aoTerminar, { silencioMs = 700, limiar = 500 } = {}) {
  let buffer = [];
  let falando = false;
  let ultimoSom = 0;

  const energia = (buf) => {
    let soma = 0;
    for (let i = 0; i < buf.length; i += 2) soma += Math.abs(buf.readInt16LE(i));
    return soma / (buf.length / 2);
  };

  return {
    alimentar(buf) {
      const agora = Date.now();
      if (energia(buf) > limiar) {
        falando = true;
        ultimoSom = agora;
      }
      if (falando) buffer.push(buf);
      if (falando && agora - ultimoSom > silencioMs) {
        falando = false;
        const trecho = Buffer.concat(buffer);
        buffer = [];
        aoTerminar(trecho);
      }
    },
  };
}

Isso funciona para um protótipo. Em produção, troque por um detector de voz (VAD) de verdade e ajuste os limites com ligações reais: ambiente barulhento, pessoa que pensa no meio da frase, áudio de viva-voz. É exatamente o assunto de latência, interrupção e silêncio.

O que a IA pode fazer durante a ligação

Um agente de voz útil não só conversa: ele consulta e age. O mesmo mecanismo de function calling que você usa no texto funciona aqui — o responder pode chamar ferramentas para ver horários livres, consultar pedido ou abrir chamado. E, com RAG, ele responde com base nas suas políticas, não no que o modelo acha.

Uma regra que muda de canal para canal: na voz, respostas curtas. Duas frases no máximo. Texto longo lido por voz sintética cansa, e o cliente interrompe. Deixe isso explícito nas instruções do modelo.

Handoff: quando a IA deve passar o caso

Não existe transferência de chamada para outro número. Então o handoff é sempre o mesmo movimento: a IA avisa, encerra a ligação e o caso segue por outro caminho.

  • Mensagem no WhatsApp, logo depois da ligação: "Vou passar seu caso para o time; em até 30 minutos alguém te chama por aqui."
  • Ticket no seu sistema, com o resumo da conversa até ali.
  • Retorno agendado por um humano, no horário que o cliente escolher.

Quando fazer isso? As mesmas regras de quando a IA deve parar de responder valem na voz, com um agravante: irritação na voz aparece antes. Pedido explícito de humano, duas respostas sem resolver, assunto sensível (cobrança contestada, reclamação séria) — em todos esses casos, passe o caso.

Quando não atender com IA

Nem toda ligação deveria cair no agente:

  • Ligações de grupo. O is_group vem no evento; o esqueleto acima ignora.
  • Fora do escopo do agente. Se a IA só sabe agendar, um cliente com problema técnico vai sair frustrado. Às vezes é melhor recusar e responder por texto.
  • Cliente que já pediu humano antes. Guarde isso no seu CRM e respeite.
  • Limite de sessões simultâneas. A plataforma tem um teto de ligações com áudio ativo ao mesmo tempo; em pico, tenha um plano B (recusar com mensagem) em vez de deixar tocar.

Transparência e LGPD

Diga logo na saudação que é um assistente virtual e, se você transcreve ou grava, que a ligação pode ser transcrita. Defina para que usa essas transcrições, por quanto tempo guarda e quem acessa. As cláusulas que um cliente corporativo vai pedir estão em API de WhatsApp e LGPD.

E um limite que não se negocia: o agente de voz é para atender quem ligou e para retornos combinados. Ligação automatizada para lista de contatos é robocall — é denunciada rápido e derruba número. A WAME não apoia esse uso. No uso certo, atendendo quem procurou você, a taxa de bloqueio é muito baixa.

Custo e onde ele está

O custo do agente de voz fica quase todo na IA: minutos de transcrição, tokens do modelo e caracteres de voz sintética. Ligações longas e respostas prolixas são o que encarece. Os cálculos de quanto custa rodar um agente de IA valem aqui, somando transcrição e voz.

Conclusão

Um agente de voz no WhatsApp é um chatbot com duas peças a mais — ouvir e falar — e uma exigência nova: tempo real. A API não oficial da WAME entrega a parte difícil do canal: o evento de chamada no webhook, o atendimento com áudio aberto e a voz do cliente em PCM 16 kHz pelo socket, com o caminho de volta pelo mesmo socket ou por arquivo. Do seu lado ficam a detecção de fala, a IA e as regras de quando passar para um humano. Comece pelo esqueleto acima, teste com ligações reais e só depois otimize a latência. Os endpoints de chamada estão na documentação, e o básico de ligar e atender está em ligação pelo WhatsApp via API.

Pronto para automatizar seu WhatsApp?

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

Começar grátis

Perguntas frequentes

Dá para uma IA atender ligação no WhatsApp?+

Sim. Com a API não oficial da WAME, a ligação chega no webhook, você atende com POST /{key}/call/accept e passa a receber a voz de quem ligou em tempo real pelo Socket.IO (evento call-audio, em PCM 16 kHz). Seu código transcreve, pede a resposta a um modelo de IA, sintetiza a voz e devolve o áudio pelo evento call-audio-in.

Em que formato chega o áudio da ligação?+

PCM de 16 bits, little-endian, mono, a 16 kHz, codificado em base64 no campo pcm do evento call-audio. O mesmo formato vale para o áudio que você envia de volta pelo evento call-audio-in. Se o seu provedor de voz usa outra taxa, como 24 kHz, é preciso reamostrar.

A IA consegue transferir a ligação para um atendente?+

Não existe transferência de chamada para outro número. O handoff é feito encerrando a ligação com um aviso e passando o caso para um humano: mensagem no WhatsApp, ticket no seu sistema ou retorno agendado. É por isso que o agente precisa saber a hora de parar.

Preciso avisar que quem atende é uma IA?+

É a prática recomendada e ajuda a cumprir a LGPD: logo na saudação, diga que a ligação é atendida por um assistente virtual e, se for o caso, que ela pode ser transcrita. Transparência também reduz reclamação e denúncia.

Posso usar isso para ligar para uma lista de contatos?+

Não. Ligação automatizada para quem não pediu é robocall, é denunciada rápido e derruba o número. A WAME não apoia esse uso. O agente de voz serve para atender quem ligou e para retornos combinados com o cliente.

Continue lendo