Agente de IA no n8n com WhatsApp: setup para produção
Monte um agente de IA no n8n com WhatsApp pronto para produção: webhook assinado, 200 imediato, proteção contra duplicatas, node AI Agent com memória e handoff.

O essencial
- Um agente de IA no n8n com WhatsApp precisa só de um node Webhook, de um node Code que verifica a assinatura, de um node AI Agent com memória e de um node HTTP Request que envia a resposta.
- Ative a opção Raw Body do node Webhook, porque a assinatura cobre os bytes exatos que a whasp enviou e um JSON serializado de novo pode falhar na verificação.
- Responda 200 logo depois da verificação, descarte duplicatas com um node Remove Duplicates usando o id do evento e envie as respostas com um Idempotency-Key, para que uma nova tentativa nunca envie duas vezes.
- Use o Postgres Chat Memory ou outra memória baseada em banco de dados em produção, porque o Simple Memory vive na memória do processo do n8n e esquece um chat depois de uma hora de silêncio.
- O handoff para um humano fica no n8n, não na whasp: mantenha uma flag por chat em uma Data table e confira essa flag antes de cada chamada à IA.
Para montar um agente de IA no n8n com WhatsApp, aponte um node Webhook para os eventos de entrada do agente, verifique a assinatura em um node Code, responda 200 na hora, descarte eventos duplicados, rode um node AI Agent com um modelo de chat e memória indexada pelo telefone do remetente e envie a resposta com um node HTTP Request. Este guia usa a whasp para o lado do WhatsApp e foi executado no n8n 2.43.8.
Por que não usar o node de WhatsApp do n8n?
O node WhatsApp Business Cloud nativo do n8n envia mensagens, templates e mídia pela WhatsApp Business Platform oficial da Meta. Desde 15 de janeiro de 2026, os termos dessa plataforma barram assistentes de IA de uso geral, como explica Meta proíbe IA no WhatsApp, e todas as opções para colocar um agente de IA no WhatsApp têm cada uma suas contrapartidas. Este guia usa um número que a whasp opera. O n8n não precisa de nenhum node especial para isso: a whasp envia eventos por POST para uma URL e recebe as respostas por uma API HTTPS simples, então os nodes Webhook e HTTP Request bastam.
A cadeia tem oito passos: Webhook, Verify signature (Code), Respond to Webhook, Text message? (If), Remove Duplicates, Is STOP? (If), AI Agent com o modelo e a memória, Send reply (HTTP Request). Executamos tudo, exceto o node do Postgres, no n8n 2.43.8 em 19 de outubro de 2026, com o modelo de linguagem e a API da whasp simulados por stubs.
Você precisa de um n8n acessível por HTTPS público (atrás de um proxy reverso, defina N8N_WEBHOOK_URL), de um número da whasp, de uma chave de API e de uma credencial de modelo.
Como receber os eventos da whasp no n8n?
Adicione um node Webhook e configure:
- HTTP Method: POST. Path:
whasp. - Respond: Using ‘Respond to Webhook’ Node.
- Options, Raw Body: ativado.
O node Webhook tem uma URL de teste e uma URL de produção. A URL de teste (/webhook-test/whasp) funciona enquanto você clica em Listen for Test Event. A URL de produção (/webhook/whasp) só funciona depois que você publica o workflow. Registre a URL de produção na whasp. A resposta inclui o segredo de assinatura (whsec_...), que você guarda como WHASP_WEBHOOK_SECRET:
curl -X PUT https://api.whasp.ai/v1/webhook \
-H "Authorization: Bearer $WHASP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://n8n.example.com/webhook/whasp"}'
O Raw Body importa. A whasp assina os bytes exatos que envia, no esquema Standard Webhooks. Com a opção ativada, o node mantém esses bytes em binary.data, ao lado do body já interpretado. Serializar de novo o JSON interpretado pode mudar espaços em branco ou a ordem das chaves e fazer a verificação falhar.
Chame POST /v1/webhook/test para enviar um evento webhook.test e ver a chegada dele.
Como verificar a assinatura em um node Code?
Adicione um node Code chamado Verify signature, no modo Run Once for All Items, com a linguagem JavaScript:
const crypto = require('crypto');
if (!$env.WHASP_WEBHOOK_SECRET) throw new Error('Set WHASP_WEBHOOK_SECRET (the whsec_ secret from whasp)');
const headers = $input.first().json.headers;
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
// Os bytes exatos que a whasp assinou: a opção Raw Body do node Webhook os guarda em binary.data.
const raw = await this.helpers.getBinaryDataBuffer(0, 'data');
const key = Buffer.from($env.WHASP_WEBHOOK_SECRET.replace(/^whsec_/, ''), 'base64');
const expected = crypto.createHmac('sha256', key)
.update(`${id}.${timestamp}.`)
.update(raw)
.digest();
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300; // 5 minutos
const signed = String(headers['webhook-signature'] ?? '').split(' ').some((part) => {
const [version, sig] = part.split(',');
if (version !== 'v1' || !sig) return false;
const given = Buffer.from(sig, 'base64');
return given.length === expected.length && crypto.timingSafeEqual(given, expected);
});
if (!id || !timestamp || !fresh || !signed) return [{ json: { valid: false } }];
const event = JSON.parse(raw.toString('utf8'));
const m = event.data?.message ?? {};
return [{
json: {
valid: true,
eventId: id,
type: event.type,
number: m.number, // o número da whasp que recebeu a mensagem
chat: m.chat, // o telefone do remetente, em E.164
name: m.sender_name ?? null,
text: m.text ?? '',
},
}];
O código segue a regra da whasp: HMAC-SHA256 sobre <id>.<timestamp>.<corpo bruto>, com a chave sendo a parte do segredo depois de whsec_ decodificada de base64, e um timestamp com no máximo cinco minutos de idade. Ele devolve valid: false em vez de lançar um erro. Um erro antes do node Respond to Webhook faz o n8n responder 500, e a whasp tentaria o evento de novo até 12 vezes. Um 401 limpo é melhor.
Duas configurações pegam muita gente de surpresa no n8n 2.x self-hosted, e as duas falham com uma mensagem clara. Desde o n8n 2.0, o node Code bloqueia variáveis de ambiente por padrão, e sem N8N_BLOCK_ENV_ACCESS_IN_NODE=false este node falha com “access to env vars denied”. Os módulos nativos também são bloqueados: sem NODE_FUNCTION_ALLOW_BUILTIN=crypto você recebe “Module ‘crypto’ is disallowed”. Com task runners externos, essa segunda variável vai na configuração do próprio runner. No n8n Cloud, o crypto é permitido. Se você não consegue definir variáveis de ambiente, crie uma variável personalizada, onde o seu plano tiver esse recurso, e leia $vars.WHASP_WEBHOOK_SECRET no lugar.
Como responder rápido e evitar duplicatas?
A whasp considera entregue qualquer 2xx recebido em até 10 segundos. Caso contrário, ela tenta de novo depois de 30 segundos, 2 minutos, 10 minutos, 30 minutos e então de hora em hora, até 12 vezes. Os eventos podem chegar duas vezes e fora de ordem. Três nodes cuidam disso.
- Respond to Webhook. Respond With: No Data. Response Code:
{{ $json.valid ? 200 : 401 }}. O node repassa a entrada depois de responder, então o workflow continua rodando enquanto a whasp já tem a resposta. - Text message? (If). Continue só quando
validfor true,typefor igual amessage.receivedetextnão estiver vazio. Todo o resto (message.sent,number.status_changed, uma foto sem texto) cai no ramo false. Liguemessage.failedenumber.status_changeda um alerta ali. - Remove Duplicates. Operation: Remove Items Processed in Previous Executions. Keep Items Where: Value Is New. Value to Dedupe On:
{{ $json.eventId }}. O histórico padrão é de 10.000 itens.
Enviamos o mesmo evento assinado duas vezes e também um evento adulterado: o primeiro recebeu uma resposta, a duplicata recebeu um 200 e nenhuma segunda execução, e o adulterado recebeu um 401. Um limite: depois que você respondeu 200, uma queda dentro do n8n perde esse evento, porque a whasp não o reenvia. Se isso importa, grave cada evento em uma tabela ou fila antes de confirmar. Webhook do WhatsApp: assinatura, novas tentativas e duplicatas cobre o padrão.
Como configurar o node AI Agent, a memória e a resposta?
AI Agent. Source for Prompt (User Message): Define below. Prompt (User Message): {{ $json.text }}. Em Options, System Message, escreva quem é o agente e como ele deve escrever, por exemplo “Você é a Ava, uma assistente simpática que conversa no WhatsApp. Só texto simples, sem Markdown. Faça uma pergunta de cada vez.” Para um prompt mais completo, veja como fazer um agente de IA escrever como gente no WhatsApp. A resposta do agente chega em $json.output.
Chat model. Anexe um OpenAI Chat Model ou um Anthropic Chat Model. Os dois carregam a lista de modelos da sua conta, então escolha ali um modelo atual.
Memory. Anexe o Postgres Chat Memory. Defina Session ID como Define below e Key como {{ $json.number }}:{{ $json.chat }}, para que cada pessoa tenha um histórico por número. A tabela padrão é n8n_chat_histories e o Context Window Length vem como 5, então aumente até onde o orçamento do seu prompt permitir. Evite o Simple Memory em produção. O código-fonte dele guarda as sessões em um map dentro do processo do n8n e apaga qualquer sessão parada há uma hora, e a documentação diz que ele não funciona em queue mode. Os servidores da whasp não guardam mensagens, então o histórico é seu, e um banco de dados sob o seu controle é o lugar certo. Memória de conversa para um agente de IA no WhatsApp aprofunda o assunto.
Send reply. Adicione um node HTTP Request:
| Campo | Valor |
|---|---|
| Method, URL | POST, https://api.whasp.ai/v1/messages |
| Authentication | Generic Credential Type, Header Auth: nome Authorization, valor Bearer wsk_... |
| Send Headers | Idempotency-Key: {{ $('Verify signature').first().json.eventId }} |
| Send Body, JSON, Using JSON | {{ { to: $('Verify signature').first().json.chat, text: $json.output } }} |
| Settings | Retry On Fail ativado, Max Tries 3, Wait Between Tries 2000 ms |
Monte o body como uma expressão que devolve um objeto. Colar o texto do modelo dentro de uma string JSON quebra na primeira aspa ou quebra de linha. A chave de idempotência torna as novas tentativas seguras: a mesma chave devolve a primeira mensagem e nunca envia duas vezes. O n8n permite no máximo 5 tentativas e 5 segundos entre elas, então cobre soluços curtos, não quedas. A whasp responde 202 e envia as mensagens de um número em ordem, com cerca de um segundo de intervalo.
Como tratar STOP, erros e rajadas de mensagens?
A whasp descadastra (opt-out) um chat por conta própria quando a pessoa envia STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, QUIT ou qualquer grafia de “opt out”, e os envios seguintes para esse chat devolvem 403 chat_opted_out. A mensagem ainda chega ao seu webhook, então adicione um node If chamado Is STOP? depois do Remove Duplicates. Defina o valor como a expressão {{ /^(STOP|UNSUBSCRIBE|OPTOUT|CANCEL|END|QUIT)$/.test($json.text) || /^opt[\s_-]*out$/i.test($json.text) }} e a condição como Boolean, is true. No ramo true, pule o modelo e envie um HTTP Request com "override_optout": true e uma frase como “Você foi descadastrado. Escreva quando quiser para conversar de novo.” Pedidos educados como “por favor, pare” ficam por sua conta.
Para falhas, abra Workflow Settings, defina um Error workflow e monte-o a partir de um node Error Trigger que avise no Slack ou por e-mail. Leia os 429: rate_limited significa mais de 100 requisições de API em 10 segundos por conta, e number_daily_limit significa que o número enviou 1.500 mensagens em 24 horas. Um 409 number_unavailable significa que o número está banido, offline ou aposentado.
Rajadas exigem uma decisão. Uma pessoa que manda três textos em cinco segundos dispara três execuções e recebe três respostas. Desenvolvedores no fórum do n8n recomendam juntar as mensagens rápidas de um cliente em um único turno (debounce) antes de chamar o modelo. Não construímos essa parte.
Como passar uma conversa para um humano (handoff)?
A whasp é só um duto: não tem caixa de entrada nem como um atendente assumir a conversa, então o estado do handoff fica no n8n. Um desenho que funciona usa uma Data table chamada chat_state, com as colunas chat e handoff. Não executamos esta parte, mas as operações dos nodes estão documentadas.
- Depois do Remove Duplicates, adicione um node Data table: Row, If Row Exists, com
chatigual a{{ $json.chat }}ehandofftrue. O ramo true encaminha a mensagem para o Slack ou para o seu helpdesk e para ali. O ramo false segue para o agente. - Marque a flag com um Upsert row da Data table quando o texto bater com uma regra como
/humano|atendente|pessoa de verdade/i, ou quando o seu agente pedir. Depois envie “Uma pessoa vai responder por aqui em breve.” - Monte um segundo workflow que dispara quando a equipe responde, chama
POST /v1/messagescom o mesmo chat e volta ohandoffpara false, com um upsert, quando houver um comando de retomada.
Confira a flag de novo logo antes de enviar a resposta do modelo, porque um handoff pode começar enquanto o modelo ainda está pensando.
O que fazer agora
Publique o workflow, registre a URL de produção dele com PUT /v1/webhook, envie POST /v1/webhook/test e depois mande uma mensagem para o seu número a partir do seu celular. Se nada chegar, GET /v1/webhook/deliveries mostra cada tentativa e o status. O quickstart percorre a API. Para o mesmo fluxo em código, veja Integração OpenAI com WhatsApp: agente GPT em TypeScript.
Perguntas
Como conectar o n8n ao WhatsApp para um agente de IA?
Receba as mensagens com um node Webhook, rode um node AI Agent e envie a resposta com um node HTTP Request que chama POST https://api.whasp.ai/v1/messages. O node WhatsApp Business Cloud do próprio n8n usa a API oficial da Meta, que não serve para um assistente de uso geral.
Por que o node Code do n8n mostra o erro Module 'crypto' is disallowed?
Desde o n8n 2.0, o node Code roda em task runners e bloqueia módulos nativos por padrão. No n8n self-hosted, defina NODE_FUNCTION_ALLOW_BUILTIN=crypto onde o task runner lê as variáveis de ambiente. O n8n Cloud já permite o crypto.
Por que o node Code do n8n mostra o erro access to env vars denied?
O n8n 2.0 bloqueia variáveis de ambiente no node Code por padrão. No n8n self-hosted, defina N8N_BLOCK_ENV_ACCESS_IN_NODE=false, ou guarde o segredo como uma variável personalizada e leia com $vars.
Como impedir que o n8n responda duas vezes à mesma mensagem do WhatsApp?
Coloque um node Remove Duplicates depois do Webhook, configurado para remover itens processados em execuções anteriores, e deduplique pelo id do evento, que vem do header webhook-id. Depois envie a resposta usando esse mesmo id como Idempotency-Key.
Qual node de memória do n8n um agente de WhatsApp deve usar?
Use o Postgres Chat Memory ou o Redis Chat Memory, com o número de telefone do remetente como chave da sessão. O Simple Memory roda dentro do processo, é descartado depois de uma hora sem uso e não funciona em queue mode.


