Webhooks
Ozzie expone un endpoint de webhook que recibe mensajes entrantes de WhatsApp enrutados a través de Evolution API. Cuando un usuario envía un mensaje a tu instancia de WhatsApp, Evolution API lo reenvía a este endpoint, Ozzie lo procesa y responde automáticamente al usuario en su idioma configurado.
Esta es la base para construir un coach financiero en WhatsApp sin tener que escribir ninguna lógica de enrutamiento de mensajes.
Cómo funciona
Usuario (WhatsApp) → Evolution API → POST /api/webhooks/evolution → Ozzie procesa + responde
↓
Usuario recibe respuesta en WhatsApp
- Un usuario envía un mensaje de WhatsApp a tu instancia de Evolution API
- Evolution API reenvía el payload del mensaje a tu URL de webhook configurada
- Ozzie verifica la firma de la solicitud, identifica al usuario por su número de teléfono y procesa el mensaje
- Ozzie envía una respuesta automática de vuelta a través de Evolution API en el idioma configurado del usuario
POST /api/webhooks/evolution
Este endpoint es llamado por Evolution API, no por tu servidor. Tu trabajo es configurar Evolution API para que apunte a él. Ozzie se encarga de todo lo demás.
Fíjate en la ruta: /api/webhooks/evolution — no /v1/.... Este endpoint está fuera del espacio de nombres REST versionado porque se ajusta al contrato de webhook de Evolution API, no al formato de solicitud estándar de Ozzie.
Guía de configuración
Paso 1: Configurar Evolution API
En el dashboard o configuración de Evolution API, establece la URL del webhook para tu instancia como:
https://tu-dominio.com/api/webhooks/evolution
Reemplaza tu-dominio.com con el dominio donde está alojado tu backend integrado con Ozzie.
Evolution API solo reenviará eventos de webhook a endpoints HTTPS. El desarrollo local requiere un túnel (ej.: ngrok, Cloudflare Tunnel o similar).
Paso 2: Establecer el secreto del webhook
En tu configuración de entorno, establece:
EVOLUTION_WEBHOOK_SECRET=tu_secreto_compartido_aqui
Este debe ser el mismo valor que configuras en Evolution API como apikey o secreto de webhook para tu instancia. Ozzie usa esto para verificar que las solicitudes entrantes son genuinamente de Evolution API y no falsificadas.
Paso 3: Registrar tus usuarios con sus números de teléfono
Para que Ozzie enrute un mensaje entrante de WhatsApp al usuario correcto, el número de teléfono del remitente debe coincidir con el campo phone_e164 de un usuario en Ozzie.
Al crear usuarios que interactuarán vía WhatsApp, incluye su número de teléfono en formato E.164:
curl -X POST https://api.ozzieapp.com/v1/users \
-H "Authorization: Bearer ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu" \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "user_789",
"name": "Sofía Mendes",
"language": "es",
"phone_e164": "+5491187654321"
}'
Si llega un mensaje de un número de teléfono que no coincide con ningún usuario registrado, Ozzie devolverá 404 a Evolution API y no se enviará ninguna respuesta.
Tipos de mensajes soportados
messageType de Evolution API | Lo que el usuario envía | Cómo lo procesa Ozzie |
|---|---|---|
conversation | Un mensaje de texto simple | Analizado como mensaje de chat o registro de transacción |
imageMessage | Una foto (ej.: un recibo) | Procesada por el analizador de imágenes de Ozzie para extraer datos de transacción |
El parsing de PDF y hojas de cálculo está disponible vía el endpoint REST de transacciones, pero actualmente no está soportado a través del webhook de WhatsApp. Para subida de documentos, dirige a tus usuarios a una interfaz web que llame a POST /transactions directamente.
Formato del payload del webhook
Este es el payload que Evolution API envía a tu endpoint. Ozzie analiza este formato automáticamente — no necesitas transformarlo.
{
"event": "messages.upsert",
"instance": "nombre_de_tu_instancia",
"data": {
"key": {
"remoteJid": "5491187654321@s.whatsapp.net",
"fromMe": false,
"id": "3EB0A1B2C3D4E5F6A7B8"
},
"messageType": "conversation",
"message": {
"conversation": "gasté $45 en el supermercado"
}
}
}
Payload de mensaje de imagen
{
"event": "messages.upsert",
"instance": "nombre_de_tu_instancia",
"data": {
"key": {
"remoteJid": "5491187654321@s.whatsapp.net",
"fromMe": false,
"id": "3EB0A1B2C3D4E5F6FFFF"
},
"messageType": "imageMessage",
"message": {
"imageMessage": {
"caption": "mi recibo del supermercado",
"mimetype": "image/jpeg",
"url": "https://media.evolution.example.com/media/abc123.jpg"
}
}
}
}
Campos clave
| Campo | Descripción |
|---|---|
data.key.remoteJid | El ID de WhatsApp del remitente. Ozzie elimina el sufijo @s.whatsapp.net para obtener el número de teléfono. |
data.key.fromMe | Si es true, este mensaje fue enviado por tu instancia (saliente). Ozzie ignora los mensajes fromMe: true. |
data.messageType | Determina cómo Ozzie procesa el cuerpo del mensaje. |
data.message.conversation | El contenido de texto (para el tipo conversation). |
Seguridad
Ozzie verifica cada solicitud de webhook entrante comprobando el header apikey contra el valor almacenado en tu variable de entorno EVOLUTION_WEBHOOK_SECRET.
POST /api/webhooks/evolution
apikey: tu_secreto_compartido_aqui
Content-Type: application/json
Si el header está ausente o el valor no coincide, Ozzie devuelve 401 Unauthorized y descarta el mensaje.
Buenas prácticas de seguridad
Usa un secreto fuerte y aleatorio. Genera al menos 32 bytes de datos aleatorios:
openssl rand -hex 32
# ej.: a7f3c8e1b2d4e5f60718293a4b5c6d7e8f901234567890abcdef01234567890
Rota los secretos periódicamente. Actualiza EVOLUTION_WEBHOOK_SECRET y la configuración de Evolution API al mismo tiempo durante una ventana de bajo tráfico para evitar mensajes perdidos durante la transición.
Usa solo HTTPS. Nunca expongas el endpoint de webhook por HTTP simple en producción. Todo el tráfico de webhook debe estar cifrado en tránsito.
Restringe el tráfico entrante por IP (opcional). Si Evolution API tiene un rango de IP de salida fijo para tu instancia, configura tu firewall o proxy inverso para permitir solo solicitudes de esas IPs.
Respuestas automáticas de Ozzie
Cuando Ozzie procesa exitosamente un mensaje entrante, envía una respuesta de vuelta a través de Evolution API automáticamente. La respuesta es generada por el mismo coach de IA que POST /chat/messages y se devuelve en el idioma configurado del usuario.
Ejemplo: mensaje de texto recibido → respuesta del coach enviada
El usuario envía (WhatsApp):
Acabo de pagar la factura de la luz — $120
Ozzie responde (WhatsApp):
¡Anotado! Registré $120 de Factura de Luz en Servicios 💡 Tu total en servicios para mayo ahora es $195, lo cual está dentro de tu presupuesto. ¡Lo estás haciendo muy bien este mes — sigue así!
Ejemplo: foto de recibo recibida → transacción analizada enviada
El usuario envía una foto de un recibo de supermercado.
Ozzie responde:
Analicé tu recibo del Supermercado: $87.40 registrado en Alimentación para el 5 de mayo. Tu total en alimentación para mayo ahora es $302. Tienes unos $98 restantes en tu presupuesto de comida para el mes.
Manejo de usuarios desconocidos
Si llega un mensaje de WhatsApp de un número de teléfono que no coincide con ningún usuario registrado:
- Ozzie devuelve
404a Evolution API - No se envía ninguna respuesta al usuario
- El evento se registra para depuración
Para integrar usuarios vía WhatsApp, necesitas un flujo de registro separado. Consulta la guía de caso de uso del Bot de WhatsApp para una implementación completa que maneja primeros mensajes y activa el registro de usuarios.
Errores
| Estado HTTP | Cuándo ocurre |
|---|---|
200 OK | Mensaje procesado exitosamente — Ozzie ha enviado o encolado una respuesta |
401 Unauthorized | El header apikey está ausente o no coincide con EVOLUTION_WEBHOOK_SECRET |
404 Not Found | No se encontró ningún usuario Ozzie con phone_e164 coincidente |
422 Unprocessable Entity | El payload está malformado o el messageType no está soportado |
500 Internal Server Error | El procesamiento falló — Evolution API reintentará según su configuración |
Evolution API típicamente reintenta las entregas de webhook fallidas. Asegúrate de que tu endpoint sea idempotente — procesar el mismo mensaje dos veces no debe crear transacciones duplicadas. Ozzie deduplica por el campo id del mensaje de Evolution API.