← Volver al blog

Automatización de ventas por Instagram y WhatsApp con bots

2026-05-10

Automatización de ventas por Instagram y WhatsApp con bots: una guía práctica para desarrolladores

La mensajería instantánea se convirtió en el canal de ventas preferido por millones de negocios. Instagram Direct y WhatsApp Business acumulan conversaciones que, manualmente, resultan imposibles de escalar. Para los equipos de ingeniería, esto representa un desafío técnico concreto: construir arquitecturas de bots que procesen consultas, cierren ventas y mantengan una experiencia conversacional sin sacrificar la escalabilidad.

Este artículo expone los fundamentos técnicos, las APIs disponibles y patrones de código probados para automatizar ventas en ambas plataformas.

Arquitectura general del sistema

Un bot de ventas robusto requiere al menos cuatro componentes: la capa de conectividad con las APIs de Meta, un motor de procesamiento de mensajes, una integración con catálogo de productos y pasarela de pagos, y un sistema de fallback para intervención humana. La elección entre arquitecturas stateless o con estado depende de la complejidad del flujo conversacional.

Para flujos simples de preguntas y respuestas, una función serverless resulta suficiente. Para carritos de compra con múltiples pasos, Redis o una base de datos documental almacenan el contexto por thread_id o phone_number_id.

WhatsApp Business API: configuración y primer bot

WhatsApp exige el uso de la API oficial de WhatsApp Business, accesible a través de Meta Business Platform o proveedores como Twilio, MessageBird o 360dialog. La API cloud de Meta, lanzada en 2022, eliminó gran parte de la fricción operativa.

Los requisitos previos incluyen: una cuenta de Meta Business verificada, una aplicación en developers.facebook.com, un número de teléfono registrado (puede ser uno proporcionado por Meta para desarrollo), y configurar el webhook para recepción de mensajes.

El endpoint de webhook debe manejar tanto la verificación inicial como las notificaciones de mensajes entrantes:

from fastapi import FastAPI, Request, Response
import hmac
import hashlib
import os

app = FastAPI()
VERIFY_TOKEN = os.environ["WHATSAPP_VERIFY_TOKEN"]
APP_SECRET = os.environ["WHATSAPP_APP_SECRET"]

@app.get("/webhook")
async def verify_webhook(request: Request):
    mode = request.query_params.get("hub.mode")
    token = request.query_params.get("hub.verify_token")
    challenge = request.query_params.get("hub.challenge")
    
    if mode == "subscribe" and token == VERIFY_TOKEN:
        return Response(content=challenge, status_code=200)
    return Response(status_code=403)

@app.post("/webhook")
async def receive_message(request: Request):
    body = await request.body()
    signature = request.headers.get("X-Hub-Signature-256", "")
    
    expected = hmac.new(
        APP_SECRET.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    
    if not hmac.compare_digest(f"sha256={expected}", signature):
        return Response(status_code=401)
    
    payload = await request.json()
    process_incoming_message(payload)
    return Response(status_code=200)

El procesamiento del mensaje extrae el contenido y determina la respuesta. Para un bot de ventas básico, un clasificador de intenciones dirige el flujo:

def process_incoming_message(payload: dict):
    entry = payload.get("entry", [])[0]
    changes = entry.get("changes", [])[0]
    value = changes.get("value", {})
    
    if "messages" not in value:
        return
    
    message = value["messages"][0]
    from_number = message["from"]
    msg_type = message["type"]
    
    if msg_type == "text":
        text = message["text"]["body"].lower()
        intent = classify_intent(text)
        
        if intent == "consulta_precio":
            handle_price_query(from_number, text)
        elif intent == "confirmar_compra":
            handle_purchase_confirmation(from_number, text)
        elif intent == "catalogo":
            send_product_catalog(from_number)
        else:
            send_fallback_message(from_number)

def classify_intent(text: str) -> str:
    keywords = {
        "consulta_precio": ["precio", "cuánto", "costo", "vale", "valor"],
        "confirmar_compra": ["comprar", "quiero", "encargar", "lo llevo"],
        "catalogo": ["catálogo", "productos", "tenés", "disponible"]
    }
    for intent, words in keywords.items():
        if any(w in text for w in words):
            return intent
    return "desconocido"

Para enviar mensajes, se utiliza el endpoint de la API cloud con el token de acceso:

import requests

WHATSAPP_API_URL = "https://graph.facebook.com/v18.0"
PHONE_NUMBER_ID = os.environ["WHATSAPP_PHONE_NUMBER_ID"]
ACCESS_TOKEN = os.environ["WHATSAPP_ACCESS_TOKEN"]

def send_whatsapp_message(to: str, message: str):
    url = f"{WHATSAPP_API_URL}/{PHONE_NUMBER_ID}/messages"
    headers = {
        "Authorization": f"Bearer {ACCESS_TOKEN}",
        "Content-Type": "application/json"
    }
    payload = {
        "messaging_product": "whatsapp",
        "recipient_type": "individual",
        "to": to,
        "type": "text",
        "text": {"body": message}
    }
    
    response = requests.post(url, headers=headers, json=payload)
    response.raise_for_status()
    return response.json()

WhatsApp permite mensajes interactivos que mejoran significativamente la conversión. Los botones de respuesta rápida restringen las opciones del usuario y reducen errores de interpretación:

def send_interactive_catalog(to: str):
    payload = {
        "messaging_product": "whatsapp",
        "recipient_type": "individual",
        "to": to,
        "type": "interactive",
        "interactive": {
            "type": "button",
            "body": {
                "text": "¿Qué categoría te interesa?"
            },
            "action": {
                "buttons": [
                    {
                        "type": "reply",
                        "reply": {
                            "id": "cat_electronica",
                            "title": "Electrónica"
                        }
                    },
                    {
                        "type": "reply",
                        "reply": {
                            "id": "cat_hogar",
                            "title": "Hogar"
                        }
                    },
                    {
                        "type": "reply",
                        "reply": {
                            "id": "cat_deportes",
                            "title": "Deportes"
                        }
                    }
                ]
            }
        }
    }
    # POST al mismo endpoint de mensajes

Instagram Messaging API: particularidades técnicas

Instagram comparte infraestructura con WhatsApp en la plataforma de Meta, pero presenta diferencias críticas. La Instagram Messaging API solo está disponible para cuentas de negocio o creadores, requiere habilitación explícita en la app de Instagram, y los mensajes directos tienen restricciones de 24 horas para respuestas proactivas (similar al concepto de session de WhatsApp).

El webhook es el mismo endpoint configurado para WhatsApp, con el campo messaging_product diferenciando la fuente. Sin embargo, Instagram introduce complejidad adicional: las historias, las menciones y los mensajes de voz requieren manejo específico.

Estructura de recepción para Instagram:

def process_instagram_message(payload: dict):
    entry = payload["entry"][0]
    messaging = entry["messaging"][0]
    
    sender_id = messaging["sender"]["id"]  # IG Scoped ID
    message = messaging.get("message", {})
    
    if message.get("is_echo"):
        return  # Ignorar ecos de mensajes propios
    
    if "text" in message:
        handle_text_message(sender_id, message["text"], platform="instagram")
    elif "attachments" in message:
        for att in message["attachments"]:
            if att["type"] == "story_mention":
                handle_story_mention(sender_id, att["payload"])
            elif att["type"] == "share":
                handle_shared_post(sender_id, att["payload"])

Un patrón crítico en Instagram es la detección de intención de compra desde interacciones sociales. Un comentario "¿Cuánto cuesta?" en una publicación de producto puede disparar un mensaje directo automatizado. Esto requiere suscripción al webhook de mentions y manejo del rate limiting estricto de Instagram (200 llamadas por usuario por hora en la versión básica de la API).

Diseño de flujos conversacionales con máquinas de estado

Los bots de ventas que gestionan carritos, opciones de envío y confirmaciones de pago necesitan persistencia de estado. Una implementación con transitions o una máquina de estado personalizada sobre Redis mantiene el contexto:

import redis
import json
from enum import Enum, auto

redis_client = redis.Redis(host='localhost', port=6379, decode_responses=True)

class ConversationState(Enum):
    INICIO = auto()
    CATALOGO_MOSTRADO = auto()
    PRODUCTO_SELECCIONADO = auto()
    CANTIDAD_SOLICITADA = auto()
    DATOS_ENVIO = auto()
    METODO_PAGO = auto()
    CONFIRMACION = auto()
    COMPLETADO = auto()

class SalesBot:
    def __init__(self, user_id: str, platform: str):
        self.user_id = user_id
        self.platform = platform
        self.key = f"bot_state:{platform}:{user_id}"
    
    def get_state(self) -> dict:
        data = redis_client.get(self.key)
        return json.loads(data) if data else {"state": "INICIO", "cart": []}
    
    def update_state(self, state: str, data: dict = None):
        current = self.get_state()
        current["state"] = state
        if data:
            current.update(data)
        redis_client.setex(self.key, 3600, json.dumps(current))
    
    def process_message(self, message: str) -> str:
        state_data = self.get_state()
        current = ConversationState[state_data["state"]]
        
        handlers = {
            ConversationState.INICIO: self.handle_inicio,
            ConversationState.CATALOGO_MOSTRADO: self.handle_catalog_selection,
            ConversationState.PRODUCTO_SELECCIONADO: self.handle_quantity,
            ConversationState.CANTIDAD_SOLICITADA: self.handle_shipping,
            ConversationState.DATOS_ENVIO: self.handle_payment,
            ConversationState.METODO_PAGO: self.handle_confirmation,
        }
        
        handler = handlers.get(current, self.handle_unknown)
        return handler(message, state_data)
    
    def handle_inicio(self, message: str, state_data: dict) -> str:
        self.update_state("CATALOGO_MOSTRADO")
        return self.format_catalog_response()
    
    def handle_quantity(self, message: str, state_data: dict) -> str:
        try:
            qty = int(message)
            if qty < 1 or qty > 10:
                return "Por favor, ingresá una cantidad entre 1 y 10 unidades."
            
            product = state_data["selected_product"]
            cart_item = {**product, "quantity": qty, "subtotal": product["price"] * qty}
            
            self.update_state("DATOS_ENVIO", {
                "cart": [cart_item],
                "selected_product": None
            })
            return f"Perfecto. {qty} unidades agregadas. ¿Dónde enviamos tu pedido? (Ciudad, dirección, código postal)"
        except ValueError:
            return "No entendí la cantidad. ¿Cuántas unidades querés?"

Integración con pasarelas de pago

El cierre de venta requiere generar un link de pago. MercadoPago, Stripe y PayPal ofrecen APIs de checkout que se integran vía webhooks de confirmación. Para WhatsApp, los mensajes de tipo template con buttons de URL resultan efectivos:

def generate_checkout_message(to: str, cart: list, total: float):
    preference = create_mercadopago_preference(cart)
    
    payload = {
        "messaging_product": "whatsapp",
        "recipient_type": "individual",
        "to": to,
        "type": "template",
        "template": {
            "name": "checkout_payment",
            "language": {"code": "es_AR"},
            "components": [
                {
                    "type": "body",
                    "parameters": [
                        {"type": "text", "text": f"${total:.2f}"},
                        {"type": "text", "text": str(len(cart))}
                    ]
                },
                {
                    "type": "button",
                    "sub_type": "url",
                    "index": "0",
                    "parameters": [
                        {"type": "text", "text": preference["id"]}
                    ]
                }
            ]
        }
    }
    return payload

def create_mercadopago_preference(cart: list) -> dict:
    import mercadopago
    sdk = mercadopago.SDK(os.environ["MP_ACCESS_TOKEN"])
    
    items = [{
        "title": item["name"],
        "quantity": item["quantity"],
        "unit_price": item["price"]
    } for item in cart]
    
    preference_data = {
        "items": items,
        "back_urls": {
            "success": "https://tudominio.com/pago/ok",
            "failure": "https://tudominio.com/pago/error"
        },
        "notification_url": "https://tudominio.com/webhooks/mercadopago",
        "auto_return": "approved"
    }
    
    return sdk.preference().create(preference_data)["response"]

Consideraciones de escalabilidad y monitoreo

Los webhooks de Meta pueden generar picos de carga. Una cola de mensajería como RabbitMQ o SQS desacopla la recepción del procesamiento. Los workers consumen la cola y ejecutan la lógica del bot sin bloquear el endpoint HTTP.

Aspectos críticos de producción:

Ejemplo de middleware de idempotencia:

def idempotent_handler(func):
    def wrapper(payload: dict):
        msg_id = extract_message_id(payload)
        if redis_client.set(f"processed:{msg_id}", "1", nx=True, ex=86400):
            return func(payload)
        return {"status": "already_processed"}
    return wrapper

@idempotent_handler
def process_incoming_message(payload: dict):
    # Lógica de procesamiento
    pass

Herramientas y frameworks complementarios

Si bien la implementación desde cero ofrece control total, existen herramientas que aceleran el desarrollo:

Conclusión

La automatización de ventas por Instagram y WhatsApp es, fundamentalmente, un problema de ingeniería de integración. Las APIs de Meta son estables pero verbosas, exigen rigor en la firma de webhooks y el cumplimiento de políticas de uso. El diferenciador no está en conectar con la API, sino en diseñar flujos conversacionales que reduzcan fricción, manejen errores con elegancia y escalen sin pérdida de contexto.

Los ejemplos de código presentados constituyen un punto de partida. En producción, cada componente requiere tests unitarios, manejo de excepciones granular y observabilidad completa. La inversión en esta infraestructura se recupera mediante la capacidad de atender cientos de conversaciones simultáneas con tiempos de respuesta subsegundos, algo imposible de replicar con atención humana exclusiva.