Automatización de ventas por Instagram y WhatsApp con bots
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:
- Idempotencia: Meta reintenta webhooks ante respuestas no 200. Guardar
message_idprocesados evita duplicados. - Rate limiting: WhatsApp impone límites de 80 mensajes por segundo para números verificados. Implementar backoff exponencial.
- Calidad de número: Los bloqueos masivos por spam degradan el quality rating de WhatsApp. Los templates deben ser aprobados previamente por Meta.
- Fallback humano: Un umbral de confianza del clasificador de intenciones o una palabra clave como "operador" debe derivar a un sistema de tickets.
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:
- LangChain / LlamaIndex: Para bots con comprensión de lenguaje natural avanzada, integrando modelos de lenguaje con el catálogo de productos.
- ManyChat, Chatfuel: Plataformas no-code/low-code que exponen APIs para extensión programática.
- N8N, Make: Herramientas de automatización que conectan webhooks con CRMs sin código, útiles para prototipado rápido.
- Evolution API: Alternativa open-source para WhatsApp que encapsula la API de Baileys (no oficial) con una interfaz REST. Útil para mercados donde la API oficial no está disponible, aunque con riesgo de baneos.
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.