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

IA que liga para confirmar agendamento no WhatsApp (e entende sim, não e remarcar)

IA que liga pelo WhatsApp para confirmar consultas e serviços: entende sim, não e remarcar, atualiza a agenda e cai para mensagem se ninguém atender.

Ver como Markdown

Uma IA que confirma agendamento por ligação no WhatsApp liga com POST /{key}/call só para clientes que escolheram esse canal, espera o evento call-ready no Socket.IO, diz o compromisso, entende a resposta — confirmar, cancelar ou remarcar — e atualiza a agenda. Se ninguém atender, o evento call-media-failed avisa e a confirmação segue por mensagem de texto. Com a API não oficial da WAME, tudo roda no número que o cliente já conhece.

A confirmação por mensagem já foi coberta em lembrete de agendamento no WhatsApp. A ligação é a camada extra para quem não responde texto — e tem regras próprias.

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.

Para quem ligar (e para quem não)

Este é o ponto que decide se a automação ajuda ou derruba o número. A regra:

  • Só para quem optou. No agendamento, pergunte: "Como prefere a confirmação: mensagem ou ligação?". Guarde a resposta.
  • Só para compromisso real, com data e hora marcadas pelo próprio cliente.
  • Uma ligação por compromisso. Não atendeu, vai por texto. Religar três vezes é o comportamento que gera denúncia.
  • Em horário razoável. Comercial, nunca cedo demais ou à noite.

Ligar automaticamente para uma lista de contatos que não espera a ligação é robocall. É denunciado rápido, derruba o número e a WAME não apoia esse uso. Feito do jeito certo — cliente que pediu, uma vez, no horário combinado —, é atendimento, e a taxa de bloqueio é muito baixa.

O fluxo completo

  1. Um job seleciona os agendamentos de amanhã com canal = "ligacao" e ainda não confirmados.
  2. Para cada um, espaçado no tempo, POST /{key}/call. A resposta volta na hora com o callId e status: "ringing".
  3. Atendeu: o Socket.IO emite call-ready com o callId. A IA fala o compromisso e ouve a resposta pelo evento call-audio.
  4. Não atendeu ou recusou: o Socket.IO emite call-media-failed. O sistema manda a confirmação por mensagem.
  5. A resposta vira ação na agenda: confirmado, cancelado ou remarcado.
  6. A ligação é encerrada com POST /{key}/call/end e o resultado é registrado.

Iniciar a ligação

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

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

const emAndamento = new Map(); // callId -> { agendamento, peerJid, tentativas }

async function ligarPara(agendamento) {
  const r = await post('/call', { to: agendamento.telefone });
  const { callId, peerJid } = r.response ?? {};
  if (!callId) {
    await confirmarPorTexto(agendamento); // falhou antes de tocar
    return;
  }
  emAndamento.set(callId, { agendamento, peerJid, tentativas: 0, historico: [] });
}

A chamada responde assim que o telefone começa a tocar, com callId e peerJid. O áudio só existe depois que a pessoa atende — por isso o resto do fluxo depende dos eventos do socket.

Ouvir o socket

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

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

socket.on('call-ready', async ({ callId }) => {
  const c = emAndamento.get(callId);
  if (!c) return;
  const { nome, servico, dataHora } = c.agendamento;
  await falar(callId,
    `Olá, ${nome}! Aqui é a assistente virtual da Clínica Exemplo. ` +
    `Estou ligando para confirmar ${servico} ${dataHora}. ` +
    `Você confirma, quer cancelar ou prefere remarcar?`);
});

socket.on('call-media-failed', async (evt) => {
  const c = evt.callId && emAndamento.get(evt.callId);
  if (!c) return;
  emAndamento.delete(evt.callId);
  await confirmarPorTexto(c.agendamento);
});

socket.on('call-audio', ({ callId, pcm }) => {
  const c = emAndamento.get(callId);
  if (c) c.detector?.alimentar(Buffer.from(pcm, 'base64'));
});

Um cuidado com o call-media-failed: quando a falha acontece antes de a ligação ganhar um identificador, o evento pode vir sem callId. Por isso, tenha também um tempo limite por ligação — se nem call-ready nem call-media-failed chegarem em um minuto ou dois, trate como não atendida e siga por texto.

O falar sintetiza a frase em PCM 16 kHz e envia pelo call-audio-in em pedaços pequenos — o mesmo reprodutor descrito em latência, interrupção e silêncio. A primeira frase também avisa que é uma assistente virtual: transparência reduz desconfiança e reclamação.

Como a saudação é quase igual para todos, dá para pré-gerar o começo ("Olá, aqui é a assistente virtual da Clínica Exemplo") e sintetizar só o trecho variável. Menos latência e menos custo.

Entender sim, não e remarcar

A resposta do cliente é curta e variada: "confirmo", "pode deixar", "não vou conseguir", "tem outro horário?", "quem é?". Depois de transcrever, peça ao modelo uma classificação fechada:

javascript
const instrucoes = `
Classifique a resposta do cliente sobre o agendamento.
Responda SOMENTE com JSON: {"intencao": "confirmar|cancelar|remarcar|duvida|nao_entendi", "preferencia": "texto livre ou vazio"}
- "pode ser", "tá certo", "confirmo" => confirmar
- "não vou", "desmarca" => cancelar
- "outro dia", "mais tarde", "tem na sexta?" => remarcar (preencha preferencia)
- perguntas sobre endereço, preparo, valor => duvida
`;

async function tratarResposta(callId, texto) {
  const c = emAndamento.get(callId);
  const { intencao, preferencia } = await classificar(instrucoes, texto);

  if (intencao === 'confirmar') {
    await agenda.confirmar(c.agendamento.id);
    await falar(callId, 'Perfeito, está confirmado. Até lá!');
    return encerrar(callId);
  }
  if (intencao === 'cancelar') {
    await agenda.cancelar(c.agendamento.id);
    await falar(callId, 'Tudo bem, cancelei. Se quiser remarcar, é só chamar por aqui.');
    return encerrar(callId);
  }
  if (intencao === 'remarcar') {
    const opcoes = await agenda.horariosLivres(c.agendamento, preferencia);
    c.opcoes = opcoes.slice(0, 3);
    return falar(callId, `Tenho ${falarOpcoes(c.opcoes)}. Qual prefere?`);
  }
  if (intencao === 'duvida') {
    await falar(callId, 'Vou te mandar essas informações por mensagem agora. Você confirma o horário?');
    await enviarInformacoes(c.agendamento);
    return;
  }
  // nao_entendi
  if (++c.tentativas >= 2) {
    await falar(callId, 'Vou te mandar a confirmação por mensagem, tudo bem? Obrigada!');
    await confirmarPorTexto(c.agendamento);
    return encerrar(callId);
  }
  await falar(callId, 'Desculpe, não entendi. Você confirma o horário, quer cancelar ou remarcar?');
}

A consulta à agenda e a remarcação são ações do seu sistema; com function calling, o próprio modelo pode decidir chamar horariosLivres e remarcar, em vez de você codificar cada ramo. Para uma confirmação simples, o if explícito acima é mais previsível — e previsibilidade vale ouro numa ligação.

Três regras de conversa que evitam frustração:

  • No máximo três opções de horário por vez. Por voz, ninguém guarda cinco.
  • Repita a decisão antes de encerrar: "Então fica quinta, às 14h. Confirmado."
  • Duas incompreensões, sai da voz. Não force; mande por mensagem.

Encerrar e registrar

javascript
async function encerrar(callId) {
  const c = emAndamento.get(callId);
  if (!c) return;
  emAndamento.delete(callId);
  setTimeout(() => post('/call/end', { callId, peerJid: c.peerJid }), 3000); // deixa a frase final tocar
  await agenda.registrarContato(c.agendamento.id, { canal: 'ligacao', resultado: c.resultado });
}

Se o cliente desligar antes, o evento de chamada com o novo status chega no webhook; limpe o estado e, se nada foi decidido, siga por texto.

O caminho do texto

A confirmação por texto é o plano B e precisa ser boa. Abra com uma mensagem simples antes de botões — para contatos que nunca conversaram com o número, mensagens interativas podem não aparecer como primeira mensagem:

javascript
async function confirmarPorTexto(a) {
  await post('/message/text', {
    to: a.telefone,
    text: `Olá, ${a.nome}! Tentamos te ligar para confirmar ${a.servico} ${a.dataHora}.`,
  });
  await post('/message/button_reply', {
    to: a.telefone,
    text: 'Você confirma?',
    buttons: [
      { type: 'quick_reply', id: `conf_${a.id}`, text: 'Confirmo' },
      { type: 'quick_reply', id: `canc_${a.id}`, text: 'Cancelar' },
      { type: 'quick_reply', id: `rem_${a.id}`, text: 'Remarcar' },
    ],
  });
}

O clique volta no webhook como interactive.button_reply.id, e a mesma lógica de agenda trata a resposta.

Ritmo das ligações

Um job que dispara cem ligações no mesmo minuto não parece atendimento. Espace:

  • Uma ligação por vez ou poucas em paralelo, com intervalo entre elas. A plataforma também limita quantas sessões de áudio rodam ao mesmo tempo.
  • Distribua ao longo do horário comercial do dia anterior, não tudo às 9h.
  • Respeite quem pediu outro horário para contato.

Se a sua API devolver 429 em algum envio de texto do plano B, é sinal para desacelerar a fila, não para tentar de novo na hora. A lógica de fila está em fila, rate limit e retry.

Medir o que importa

  • Taxa de atendimento das ligações.
  • Taxa de confirmação por canal (voz vs texto).
  • Faltas antes e depois de ligar.
  • Pedidos de "prefiro mensagem" — sinal para mudar o canal padrão desse cliente.

Se a voz não reduz faltas mais do que o texto para o seu público, fique no texto: é mais barato. A ligação faz mais sentido para agendamentos de alto valor e para quem não responde mensagens.

Conclusão

Uma IA que liga para confirmar agendamento é uma conversa curta e bem delimitada: dizer o compromisso, entender confirmar, cancelar ou remarcar, e agir na agenda. A API não oficial da WAME dá as peças — POST /{key}/call para ligar, call-ready e call-media-failed no Socket.IO para saber o que aconteceu, call-audio e call-audio-in para ouvir e falar. O que faz dar certo é o seu lado: ligar só para quem escolheu esse canal, uma vez, no horário certo, e cair para mensagem sem insistir. O agente de voz completo está em IA para atender ligação no WhatsApp, e o básico de chamadas, 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

Posso fazer uma IA ligar para confirmar consultas pelo WhatsApp?+

Sim, para clientes que escolheram receber a confirmação por ligação. Com a API não oficial da WAME você liga com POST /{key}/call, recebe o aviso de atendimento pelo Socket.IO (call-ready), ouve a resposta pelo evento call-audio e fala pelo call-audio-in. A IA entende confirmar, cancelar ou remarcar e atualiza a agenda.

E se o cliente não atender?+

A API avisa pelo Socket.IO com o evento call-media-failed quando a ligação não é atendida ou é recusada. Nesse caso, mande a confirmação por mensagem de texto com botões. Não fique religando: uma tentativa por voz e o resto por texto.

Isso não é robocall?+

Não, se for feito para quem pediu. Robocall é ligar automaticamente para quem não espera a ligação. Aqui a ligação é para um cliente com horário marcado que optou por confirmação por voz, no horário combinado e uma vez. A WAME não apoia ligação automatizada para listas.

Como a IA entende a resposta do cliente?+

A fala é transcrita e um modelo classifica a intenção em confirmar, cancelar, remarcar ou não entendido. Para remarcar, a IA consulta os horários livres por function calling e oferece duas ou três opções. Se não entender duas vezes, encerra e manda por mensagem.

Ligação funciona melhor que mensagem para confirmar?+

Depende do público. Para quem não lê mensagens com frequência, pessoas mais velhas ou agendamentos de alto valor, a voz costuma funcionar melhor. Para o resto, a mensagem com botões é mais barata e suficiente. O ideal é deixar o cliente escolher o canal.

Continue lendo