Atendimentotutorial

Como Integrar Agentes de IA com a WhatsApp Business API: Guia Técnico Completo

13 min de leitura

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:

  1. Conta no Meta Business Manager: acesse business.facebook.com e crie ou use uma conta existente
  2. Aplicativo no Meta for Developers: acesse developers.facebook.com, crie um app do tipo "Business" e adicione o produto "WhatsApp"
  3. 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
  4. 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:

  1. Webhook endpoint: URL no seu servidor que recebe eventos do WhatsApp (mensagens recebidas, status de entrega, etc.)
  2. Motor do agente de IA: o LLM ou plataforma que processa a mensagem e gera a resposta
  3. Gestor de sessões: mantém o contexto da conversa por usuário
  4. 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:

  1. Acesse WhatsApp → Configuração
  2. Em Webhooks, clique em Configurar
  3. Insira a URL do seu endpoint (precisa ser HTTPS)
  4. Defina um verify_token — uma string aleatória que você escolhe para validar que a requisição veio da Meta
  5. 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", "")

Halk

Crie seu agente de IA em minutos

A Halk é a plataforma SaaS para criar, operar e evoluir agentes de IA para qualquer tipo de negócio. Poder máximo com a maior facilidade de uso.

Começar gratuitamente

Continue lendo