Como Integrar Agentes de IA com a WhatsApp Business API: Guia Técnico Completo
A WhatsApp Business API não funciona como um chatbot simples que você ativa com um clique. Ela é uma infraestrutura de comunicação corporativa que exige configuração técnica específica — e, quando integrada a um agente de IA, se transforma no canal de atendimento mais poderoso que uma empresa pode ter. Com mais de 147 milhões de usuários ativos no Brasil (Statista, 2025), o WhatsApp já é onde seus clientes estão. O agente de IA é o que faz esse canal trabalhar 24 horas por dia, sem fila, sem perda de conversa e com qualidade consistente.
Este guia cobre tudo que você precisa para colocar essa integração em produção: desde os pré-requisitos e a arquitetura técnica, passando pela configuração de webhooks, gestão de sessões e tratamento de diferentes tipos de mídia, até os fluxos de escalada humana e as métricas que indicam se está funcionando bem.
Por que Integrar Agentes de IA ao WhatsApp Business API
Antes de entrar na parte técnica, é importante entender o que muda quando você usa a API oficial — e não soluções não-oficiais baseadas em automação de WhatsApp Web.
A versão oficial da WhatsApp Business API (agora chamada de Cloud API pela Meta) oferece:
- Confiabilidade: sem risco de banimento por uso de automação não autorizada
- Escala real: suporte a milhares de conversas simultâneas
- Recursos avançados: templates de mensagem, botões interativos, listas, produtos de catálogo
- Conformidade com LGPD: dados gerenciados via infraestrutura da Meta ou de BSPs homologados
- Integração com sistemas: webhooks, APIs REST e suporte nativo a sistemas de CRM
Soluções informais (como as baseadas em WPPConnect ou Baileys) funcionam para testes ou volumes muito baixos. Para operações sérias, a API oficial é o caminho — e é com ela que este guia trabalha.
Para entender como como criar um agente de IA para WhatsApp do zero, consulte nosso guia dedicado ao tema. Aqui o foco é a integração técnica com a API.
Pré-requisitos: O que Você Precisa Antes de Começar
Conta e Acesso à API
Para usar a WhatsApp Business API, você precisa de:
- Conta no Meta Business Manager: acesse business.facebook.com e crie ou use uma conta existente
- Aplicativo no Meta for Developers: acesse developers.facebook.com, crie um app do tipo "Business" e adicione o produto "WhatsApp"
- Número de telefone exclusivo: o número não pode estar associado a nenhuma outra conta do WhatsApp (pessoal ou Business app). Pode ser uma linha nova ou um número migrado
- Verificação da empresa: para limites de mensagens mais altos, a empresa precisa estar verificada no Meta Business Manager
Acesso à Cloud API vs. BSP
A Meta oferece duas formas de acessar a API:
| Opção | Quando usar | Prós | Contras |
|---|---|---|---|
| Cloud API (Meta) | Maioria dos casos | Gratuito para hospedar, atualizações automáticas | Requer integração técnica direta |
| BSP (Business Solution Provider) | Quando você quer suporte gerenciado | Suporte, infraestrutura pronta, onboarding assistido | Custo adicional por mensagem ou assinatura |
Para a maioria das empresas que já têm equipe técnica, a Cloud API direta é a escolha mais eficiente. Plataformas como a Halk abstraem grande parte dessa configuração, mas entender os fundamentos é essencial para operar corretamente.
Credenciais Necessárias
Anote essas informações do painel do Meta for Developers — você vai precisar delas em cada chamada à API:
WHATSAPP_BUSINESS_ACCOUNT_ID(ID da conta WhatsApp Business)PHONE_NUMBER_ID(ID do número de telefone específico)ACCESS_TOKEN(token de acesso permanente — gere via System User no Business Manager)APP_SECRET(para validar assinaturas de webhook)
Arquitetura da Integração: Visão Geral
A integração entre um agente de IA e a WhatsApp Business API segue um fluxo bidirecional:
Usuário → WhatsApp → Meta Cloud API → Webhook (seu servidor) → Agente de IA → Resposta → Meta Cloud API → WhatsApp → Usuário
Os componentes principais são:
- Webhook endpoint: URL no seu servidor que recebe eventos do WhatsApp (mensagens recebidas, status de entrega, etc.)
- Motor do agente de IA: o LLM ou plataforma que processa a mensagem e gera a resposta
- Gestor de sessões: mantém o contexto da conversa por usuário
- Sender service: envia a resposta de volta via API REST da Meta
Para entender como como escalar atendimento no WhatsApp Business com IA em operações de alto volume, veja o guia específico sobre infraestrutura de escala.
Passo 1: Configurar o Webhook
O webhook é o ponto central de entrada de todas as mensagens. Quando um usuário envia uma mensagem para seu número, a Meta faz uma requisição POST para a URL que você configurou.
Configuração no Meta for Developers
No painel do seu app:
- Acesse WhatsApp → Configuração
- Em Webhooks, clique em Configurar
- Insira a URL do seu endpoint (precisa ser HTTPS)
- Defina um
verify_token— uma string aleatória que você escolhe para validar que a requisição veio da Meta - Selecione os campos a assinar. Para atendimento com agente de IA, assine no mínimo:
messages
Implementação do Endpoint de Verificação
A Meta faz uma chamada GET ao seu webhook para verificar a URL. Você precisa responder corretamente:
from flask import Flask, request, jsonify
app = Flask(__name__)
VERIFY_TOKEN = "seu_verify_token_aqui"
@app.route("/webhook", methods=["GET"])
def verify_webhook():
mode = request.args.get("hub.mode")
token = request.args.get("hub.verify_token")
challenge = request.args.get("hub.challenge")
if mode == "subscribe" and token == VERIFY_TOKEN:
return challenge, 200
return "Verificação falhou", 403
Implementação do Endpoint de Recebimento
import hmac
import hashlib
import json
APP_SECRET = "seu_app_secret_aqui"
def validate_signature(payload, signature):
expected = hmac.new(
APP_SECRET.encode("utf-8"),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@app.route("/webhook", methods=["POST"])
def receive_message():
signature = request.headers.get("X-Hub-Signature-256", "")
if not validate_signature(request.data, signature):
return "Assinatura inválida", 403
data = request.get_json()
# Confirme o recebimento imediatamente (a Meta requer resposta em até 20 segundos)
process_webhook_async(data) # processe de forma assíncrona
return jsonify({"status": "ok"}), 200
Ponto crítico: a Meta exige que seu endpoint responda com 200 OK em até 20 segundos. Se demorar mais, ela vai reenviar a mensagem e você pode processar duplicatas. Sempre responda imediatamente e processe o payload de forma assíncrona (com uma fila como Redis Queue, Celery ou AWS SQS).
Passo 2: Parsear o Payload de Mensagem
O payload que você recebe tem uma estrutura específica. Entender como parseá-lo corretamente evita bugs que só aparecem em produção.
Estrutura do Payload
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
"changes": [
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999999999",
"phone_number_id": "PHONE_NUMBER_ID"
},
"contacts": [
{
"profile": { "name": "João Silva" },
"wa_id": "5511988887777"
}
],
"messages": [
{
"from": "5511988887777",
"id": "wamid.XXXXX",
"timestamp": "1714300000",
"text": { "body": "Olá, quero saber sobre o plano Pro" },
"type": "text"
}
]
},
"field": "messages"
}
]
}
]
}
Função de Extração
def extract_message_data(payload):
try:
entry = payload["entry"][0]
change = entry["changes"][0]["value"]
# Ignore se não for uma mensagem (pode ser status de entrega)
if "messages" not in change:
return None
message = change["messages"][0]
contact = change["contacts"][0]
return {
"message_id": message["id"],
"from": message["from"],
"name": contact["profile"]["name"],
"timestamp": message["timestamp"],
"type": message["type"],
"content": extract_content(message)
}
except (KeyError, IndexError):
return None
def extract_content(message):
msg_type = message["type"]
if msg_type == "text":
return {"text": message["text"]["body"]}
elif msg_type == "image":
return {"media_id": message["image"]["id"], "caption": message["image"].get("caption", "")}
elif msg_type == "audio":
return {"media_id": message["audio"]["id"]}
elif msg_type == "document":
return {"media_id": message["document"]["id"], "filename": message["document"].get("filename", "")}
elif msg_type == "interactive":
if message["interactive"]["type"] == "button_reply":
return {"button_id": message["interactive"]["button_reply"]["id"],
"button_text": message["interactive"]["button_reply"]["title"]}
elif message["interactive"]["type"] == "list_reply":
return {"list_id": message["interactive"]["list_reply"]["id"],
"list_text": message["interactive"]["list_reply"]["title"]}
return {"raw": message}
Passo 3: Gestão de Sessões e Contexto
Um agente de IA precisa de contexto para funcionar bem. O WhatsApp não mantém estado — cada mensagem chega de forma independente. Você precisa implementar um gestor de sessões que mantenha o histórico da conversa por usuário.
Estrutura de Sessão
import redis
import json
redis_client = redis.Redis(host="localhost", port=6379, db=0)
SESSION_TTL = 3600 # 1 hora de inatividade encerra a sessão
def get_session(user_id):
session_data = redis_client.get(f"session:{user_id}")
if session_data:
return json.loads(session_data)
return {
"user_id": user_id,
"messages": [],
"state": "active",
"created_at": None,
"metadata": {}
}
def save_session(user_id, session):
redis_client.setex(
f"session:{user_id}",
SESSION_TTL,
json.dumps(session)
)
def add_to_session(user_id, role, content):
session = get_session(user_id)
session["messages"].append({
"role": role, # "user" ou "assistant"
"content": content
})
# Mantenha apenas as últimas 20 mensagens para não explodir o contexto do LLM
session["messages"] = session["messages"][-20:]
save_session(user_id, session)
return session
Janela de Sessão de 24 Horas
A WhatsApp Business API tem uma regra importante: você só pode enviar mensagens livres (não templates) dentro de uma janela de 24 horas após a última mensagem do usuário. Fora desse período, você precisa usar um template aprovado.
from datetime import datetime, timedelta
def is_within_session_window(last_user_message_timestamp):
last_message_time = datetime.fromtimestamp(int(last_user_message_timestamp))
return datetime.now() - last_message_time < timedelta(hours=24)
def send_message(user_id, content, last_user_timestamp):
if is_within_session_window(last_user_timestamp):
# Pode enviar mensagem livre
return send_free_message(user_id, content)
else:
# Precisa usar template aprovado
return send_template_message(user_id, "template_reengajamento", [])
Passo 4: Enviar Respostas via API
Enviar Mensagem de Texto
import requests
ACCESS_TOKEN = "seu_access_token"
PHONE_NUMBER_ID = "seu_phone_number_id"
BASE_URL = f"https://graph.facebook.com/v19.0/{PHONE_NUMBER_ID}/messages"
def send_text_message(to, text):
payload = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": to,
"type": "text",
"text": {
"preview_url": False,
"body": text
}
}
response = requests.post(
BASE_URL,
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=payload
)
return response.json()
Enviar Mensagem com Botões Interativos
Botões são fundamentais para guiar o usuário em fluxos estruturados — como escolha de departamento, confirmação de pedido ou seleção de horário:
def send_button_message(to, body_text, buttons):
"""
buttons: lista de dicts com {"id": "btn_id", "title": "Texto do Botão"}
Máximo de 3 botões por mensagem.
"""
payload = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": to,
"type": "interactive",
"interactive": {
"type": "button",
"body": {"text": body_text},
"action": {
"buttons": [
{
"type": "reply",
"reply": {"id": btn["id"], "title": btn["title"]}
}
for btn in buttons[:3] # máximo 3
]
}
}
}
response = requests.post(
BASE_URL,
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=payload
)
return response.json()
Enviar Lista Interativa
Para menus com mais opções (até 10 itens):
def send_list_message(to, body_text, button_label, sections):
"""
sections: lista de dicts com {"title": "Seção", "rows": [{"id": "id1", "title": "Item"}]}
"""
payload = {
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": to,
"type": "interactive",
"interactive": {
"type": "list",
"body": {"text": body_text},
"action": {
"button": button_label,
"sections": sections
}
}
}
response = requests.post(
BASE_URL,
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=payload
)
return response.json()
Marcar Mensagem como Lida
Sempre marque as mensagens como lidas após processá-las. Isso melhora a experiência do usuário (ele vê o "visto" azul) e é sinal de que o sistema está funcionando:
def mark_as_read(message_id):
payload = {
"messaging_product": "whatsapp",
"status": "read",
"message_id": message_id
}
requests.post(
BASE_URL,
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=payload
)
Passo 5: Conectar ao Agente de IA
Com o webhook configurado e o sistema de envio funcionando, o próximo passo é conectar tudo ao motor do agente de IA.
Fluxo Completo de Processamento
async def process_incoming_message(payload):
message_data = extract_message_data(payload)
if not message_data:
return # Status de entrega ou outro evento — ignore
user_id = message_data["from"]
message_id = message_data["message_id"]
# 1. Marcar como lida imediatamente
mark_as_read(message_id)
# 2. Mostrar "digitando..." (simulação via delay de resposta)
# A API do WhatsApp não tem endpoint nativo de typing indicator,
# mas um delay de 1–2 segundos melhora a experiência
# 3. Adicionar mensagem ao histórico de sessão
content_text = extract_text_from_content(message_data)
session = add_to_session(user_id, "user", content_text)
# 4. Verificar se está em escalada humana
if session.get("state") == "human_handoff":
route_to_human_agent(user_id, content_text)
return
# 5. Gerar resposta com o agente de IA
response = await generate_ai_response(
user_id=user_id,
messages=session["messages"],
user_name=message_data["name"]
)
# 6. Adicionar resposta ao histórico
add_to_session(user_id, "assistant", response["text"])
# 7. Enviar resposta
if response["type"] == "text":
send_text_message(user_id, response["text"])
elif response["type"] == "buttons":
send_button_message(user_id, response["text"], response["buttons"])
# 8. Verificar se o agente decidiu escalar para humano
if response.get("escalate"):
trigger_human_handoff(user_id, session)
Integração com LLM
import openai # ou o SDK da sua plataforma de IA
SYSTEM_PROMPT = """Você é um assistente de atendimento da [Nome da Empresa].
Responda em português brasileiro, de forma cordial e objetiva.
Se não souber a resposta, diga que vai verificar e transferir para um atendente.
Quando identificar que o usuário precisa de suporte humano, inclua [ESCALAR] no início da sua resposta."""
async def generate_ai_response(user_id, messages, user_name):
formatted_messages = [
{"role": "system", "content": SYSTEM_PROMPT}
] + [
{"role": msg["role"], "content": msg["content"]}
for msg in messages
]
response = await openai.ChatCompletion.acreate(
model="gpt-4o",
messages=formatted_messages,
temperature=0.3, # menor temperatura = mais consistência em atendimento
max_tokens=500
)
text = response.choices[0].message.content
# Verificar se o agente sinalizou escalada
should_escalate = text.startswith("[ESCALAR]")
clean_text = text.replace("[ESCALAR]", "").strip()
return {
"type": "text",
"text": clean_text,
"escalate": should_escalate
}
Fluxos Essenciais para Implementar
Transferência para Atendente Humano
Nenhum agente de IA deve operar sem um mecanismo de escalada. Para configurar corretamente os fluxos de transferência humana em agentes de IA, veja o guia dedicado. Na integração com o WhatsApp, o básico é:
def trigger_human_handoff(user_id, session):
# 1. Marcar sessão como em escalada
session["state"] = "human_handoff"
save_session(user_id, session)
# 2. Notificar o time humano (via webhook interno, e-mail, Slack, etc.)
notify_human_agent({
"user_id": user_id,
"conversation_history": session["messages"],
"reason": "Escalada solicitada pelo agente de IA"
})
# 3. Informar o usuário
send_text_message(
user_id,
"Vou transferir você para um de nossos atendentes agora. "
"Em instantes alguém estará com você. 👋"
)
Verificação de Número antes de Enviar
Antes de disparar mensagens proativamente (notificações, follow-ups), verifique se o número está registrado no WhatsApp:
def check_whatsapp_number(phone_number):
url = f"https://graph.facebook.com/v19.0/{PHONE_NUMBER_ID}/contacts"
response = requests.post(
url,
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={
"blocking": "wait",
"contacts": [f"+{phone_number}"],
"force_check": False
}
)
result = response.json()
contacts = result.get("contacts", [])
if contacts and contacts[0].get("status") == "valid":
return contacts[0]["wa_id"] # retorna o WA ID formatado
return None
Templates de Mensagem para Contato Proativo
Para enviar mensagens fora da janela de 24 horas, você precisa de templates aprovados pela Meta. Exemplo de envio:
def send_template_message(to, template_name, language_code, components=None):
payload = {
"messaging_product": "whatsapp",
"to": to,
"type": "template",
"template": {
"name": template_name,
"language": {"code": language_code},
"components": components or []
}
}
response = requests.post(
BASE_URL,
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
},
json=payload
)
return response.json()
# Exemplo de uso — enviar lembrete de consulta
def send_appointment_reminder(to, patient_name, appointment_date):
send_template_message(
to=to,
template_name="lembrete_consulta",
language_code="pt_BR",
components=[
{
"type": "body",
"parameters": [
{"type": "text", "text": patient_name},
{"type": "text", "text": appointment_date}
]
}
]
)
Para saber como configurar a base de conhecimento do agente de IA que alimenta as respostas — especialmente importante para o atendimento via WhatsApp — veja nosso guia específico.
Tratamento de Mídia (Áudio, Imagem e Documento)
Usuários do WhatsApp enviam muito mais do que texto. Ignorar mídia é um erro comum que frustra usuários.
Download de Mídia
def download_media(media_id):
# Passo 1: obter URL temporária da mídia
url_response = requests.get(
f"https://graph.facebook.com/v19.0/{media_id}",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}
)
media_url = url_response.json()["url"]
mime_type = url_response.json()["mime_type"]
# Passo 2: baixar o arquivo (URL expira em ~5 minutos)
file_response = requests.get(
media_url,
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}
)
return file_response.content, mime_type
Transcrição de Áudio com Whisper
Áudios de voz são muito comuns no WhatsApp brasileiro. Transcreva e processe como texto:
import openai
import tempfile
import os
def transcribe_audio(media_id):
audio_bytes, mime_type = download_media(media_id)
# Salva temporariamente
extension = "ogg" # WhatsApp usa OGG/Opus por padrão
with tempfile.NamedTemporaryFile(suffix=f".{extension}", delete=False) as tmp:
tmp.write(audio_bytes)
tmp_path = tmp.name
try:
with open(tmp_path, "rb") as audio_file:
transcript = openai.Audio.transcribe(
model="whisper-1",
file=audio_file,
language="pt"
)
return transcript["text"]
finally:
os.unlink(tmp_path)
# Integrar ao extract_content
def extract_text_from_content(message_data):
content = message_data["content"]
msg_type = message_data["type"]
if msg_type == "text":
return content["text"]
elif msg_type == "audio":
return f"[Áudio transcrito]: {transcribe_audio(content['media_id'])}"
elif msg_type == "image":
caption = content.get("caption", "")