- README principal con descripción del proyecto - Documento de diseño completo (arquitectura, DB, flujos) - Documentación de API REST y WebSocket - Guía del Flow Builder (30+ tipos de nodos) - Documentación de integración con Odoo - Guía de despliegue con Docker - Esquema de base de datos - Estructura de carpetas del proyecto - Archivo .env.example con todas las variables Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Flow Builder - Guía Completa
Introducción
El Flow Builder es el editor visual para crear chatbots sin código. Permite diseñar flujos de conversación arrastrando y conectando nodos.
Interfaz
┌─────────────────────────────────────────────────────────────────────┐
│ Flujo: Bienvenida [Guardar] [Probar] [Activar] │
├───────────┬─────────────────────────────────────────────────────────┤
│ NODOS │ CANVAS │
│ ───────── │ │
│ 📨 Mensaje│ [Nodos conectados con líneas] │
│ 🔘 Botones│ │
│ 📋 Lista │ │
│ ❓ Input │ │
│ ⑂ Condición │
│ ... │ │
├───────────┼─────────────────────────────────────────────────────────┤
│ │ PANEL DE PROPIEDADES (al seleccionar un nodo) │
└───────────┴─────────────────────────────────────────────────────────┘
Tipos de Nodos
Nodos de Trigger (Inicio)
welcome
Inicia el flujo cuando un contacto escribe por primera vez.
{
"type": "welcome",
"id": "trigger_1",
"data": {
"label": "Primer mensaje"
}
}
keyword
Inicia el flujo cuando el mensaje contiene palabras clave.
{
"type": "keyword",
"id": "trigger_2",
"data": {
"keywords": ["precio", "costo", "cuanto"],
"match_type": "contains"
}
}
Match types:
exact- Coincidencia exactacontains- Contiene la palabrastarts_with- Empieza conregex- Expresión regular
fallback
Se activa cuando ningún otro flujo coincide.
{
"type": "fallback",
"id": "trigger_3",
"data": {
"label": "No entendido"
}
}
event
Se activa por eventos externos (webhook, Odoo).
{
"type": "event",
"id": "trigger_4",
"data": {
"event_type": "odoo_order_confirmed",
"filters": {
"amount_total": { "gt": 1000 }
}
}
}
Nodos de Mensaje
text
Envía un mensaje de texto.
{
"type": "text",
"id": "msg_1",
"data": {
"content": "¡Hola {{contact.name}}! 👋\n\n¿En qué te puedo ayudar?",
"typing_delay": 1500
}
}
Variables disponibles:
{{contact.name}}- Nombre del contacto{{contact.phone}}- Teléfono{{agent.name}}- Nombre del agente{{variables.xxx}}- Variables capturadas
image
Envía una imagen.
{
"type": "image",
"id": "msg_2",
"data": {
"url": "https://example.com/producto.jpg",
"caption": "Nuestro producto estrella"
}
}
video
Envía un video.
{
"type": "video",
"id": "msg_3",
"data": {
"url": "https://example.com/demo.mp4",
"caption": "Video demostrativo"
}
}
document
Envía un documento (PDF, Excel, etc).
{
"type": "document",
"id": "msg_4",
"data": {
"url": "https://example.com/catalogo.pdf",
"filename": "Catálogo 2024.pdf"
}
}
location
Envía una ubicación.
{
"type": "location",
"id": "msg_5",
"data": {
"latitude": 19.4326,
"longitude": -99.1332,
"name": "Nuestra Oficina",
"address": "Av. Reforma 123, CDMX"
}
}
buttons
Envía mensaje con botones (máximo 3).
{
"type": "buttons",
"id": "msg_6",
"data": {
"content": "¿Qué necesitas?",
"buttons": [
{ "id": "ventas", "text": "Ventas" },
{ "id": "soporte", "text": "Soporte" },
{ "id": "otro", "text": "Otro" }
]
}
}
list
Envía mensaje con lista de opciones (máximo 10).
{
"type": "list",
"id": "msg_7",
"data": {
"content": "Selecciona una categoría:",
"button_text": "Ver opciones",
"sections": [
{
"title": "Productos",
"rows": [
{ "id": "laptops", "title": "Laptops", "description": "Computadoras portátiles" },
{ "id": "phones", "title": "Teléfonos", "description": "Smartphones" }
]
},
{
"title": "Servicios",
"rows": [
{ "id": "soporte", "title": "Soporte Técnico" },
{ "id": "garantia", "title": "Garantías" }
]
}
]
}
}
template
Usa un mensaje guardado como template.
{
"type": "template",
"id": "msg_8",
"data": {
"template_id": "uuid-del-template",
"variables": {
"producto": "{{variables.producto_seleccionado}}"
}
}
}
Nodos de Lógica
condition
Bifurca según condiciones.
{
"type": "condition",
"id": "cond_1",
"data": {
"conditions": [
{
"groups": [
{
"field": "{{message.text}}",
"operator": "contains",
"value": "precio"
}
],
"logic": "AND"
}
],
"outputs": ["true", "false"]
}
}
Operadores:
- Texto:
equals,not_equals,contains,not_contains,starts_with,ends_with,regex - Número:
eq,neq,gt,gte,lt,lte,between - Lista:
in,not_in,is_empty,is_not_empty - General:
exists,not_exists,is_null,is_not_null
switch
Múltiples salidas según valor.
{
"type": "switch",
"id": "switch_1",
"data": {
"field": "{{variables.departamento}}",
"cases": [
{ "value": "ventas", "output": "ventas" },
{ "value": "soporte", "output": "soporte" },
{ "value": "cobranza", "output": "cobranza" }
],
"default_output": "default"
}
}
wait_input
Espera respuesta del usuario y la guarda.
{
"type": "wait_input",
"id": "input_1",
"data": {
"variable": "nombre_cliente",
"timeout_seconds": 300,
"timeout_output": "timeout",
"validation": {
"type": "text",
"min_length": 2,
"max_length": 100
},
"error_message": "Por favor ingresa un nombre válido."
}
}
delay
Pausa antes de continuar.
{
"type": "delay",
"id": "delay_1",
"data": {
"seconds": 3,
"random_range": [2, 5]
}
}
set_variable
Asigna o modifica una variable.
{
"type": "set_variable",
"id": "set_1",
"data": {
"variable": "contador",
"operation": "increment",
"value": 1
}
}
Operaciones:
set- Asignar valorincrement- Incrementardecrement- Decrementarappend- Agregar a listaconcat- Concatenar texto
random
A/B Testing - bifurca aleatoriamente.
{
"type": "random",
"id": "ab_1",
"data": {
"test_name": "Test saludo",
"variants": [
{ "name": "A", "weight": 50, "output": "variant_a" },
{ "name": "B", "weight": 50, "output": "variant_b" }
],
"track_metric": "response_rate"
}
}
loop
Repetir bloque.
{
"type": "loop",
"id": "loop_1",
"data": {
"max_iterations": 3,
"condition": {
"field": "{{variables.confirmado}}",
"operator": "not_equals",
"value": "si"
},
"outputs": ["continue", "exit"]
}
}
go_to
Salta a otro flujo o nodo.
{
"type": "go_to",
"id": "goto_1",
"data": {
"target_type": "flow",
"target_id": "uuid-del-flujo",
"pass_context": true
}
}
sub_flow
Ejecuta un sub-flujo y retorna.
{
"type": "sub_flow",
"id": "sub_1",
"data": {
"flow_id": "uuid-del-subflujo",
"input_variables": {
"producto_id": "{{variables.producto}}"
},
"output_variable": "resultado_subflujo"
}
}
javascript
Ejecuta código JavaScript personalizado.
{
"type": "javascript",
"id": "js_1",
"data": {
"code": "const total = variables.precio * variables.cantidad;\nreturn { total, descuento: total > 1000 ? 0.1 : 0 };",
"output_variable": "calculo"
}
}
Nodos de Validación
validate_email
Valida formato de email.
{
"type": "validate_email",
"id": "val_1",
"data": {
"input": "{{message.text}}",
"output_variable": "email_valido",
"outputs": ["valid", "invalid"]
}
}
validate_phone
Valida formato de teléfono.
{
"type": "validate_phone",
"id": "val_2",
"data": {
"input": "{{message.text}}",
"country": "MX",
"output_variable": "telefono_valido",
"outputs": ["valid", "invalid"]
}
}
validate_number
Valida número en rango.
{
"type": "validate_number",
"id": "val_3",
"data": {
"input": "{{message.text}}",
"min": 1,
"max": 100,
"output_variable": "cantidad",
"outputs": ["valid", "invalid"]
}
}
validate_date
Valida formato de fecha.
{
"type": "validate_date",
"id": "val_4",
"data": {
"input": "{{message.text}}",
"format": "DD/MM/YYYY",
"min_date": "today",
"max_date": "+30days",
"output_variable": "fecha",
"outputs": ["valid", "invalid"]
}
}
validate_options
Valida contra lista de opciones.
{
"type": "validate_options",
"id": "val_5",
"data": {
"input": "{{message.text}}",
"options": ["1", "2", "3", "uno", "dos", "tres"],
"case_insensitive": true,
"output_variable": "opcion",
"outputs": ["valid", "invalid"]
}
}
retry_input
Reintenta captura con mensaje de error.
{
"type": "retry_input",
"id": "retry_1",
"data": {
"max_retries": 3,
"error_message": "No entendí tu respuesta. Por favor intenta de nuevo.",
"final_fallback": "transfer",
"outputs": ["continue", "max_retries"]
}
}
Nodos de Acción
transfer
Transfiere a agente humano.
{
"type": "transfer",
"id": "act_1",
"data": {
"target_type": "queue",
"target_id": "uuid-de-cola",
"priority": "high",
"message": "Te comunico con un agente. Por favor espera."
}
}
close
Cierra la conversación.
{
"type": "close",
"id": "act_2",
"data": {
"resolution": "resolved",
"send_csat": true,
"message": "¡Gracias por contactarnos! ¿Hay algo más en que pueda ayudarte?"
}
}
tag
Agrega o quita etiquetas.
{
"type": "tag",
"id": "act_3",
"data": {
"add": ["lead", "interesado"],
"remove": ["nuevo"]
}
}
note
Agrega nota interna.
{
"type": "note",
"id": "act_4",
"data": {
"content": "Cliente interesado en {{variables.producto}}. Presupuesto: {{variables.presupuesto}}"
}
}
notify
Envía notificación.
{
"type": "notify",
"id": "act_5",
"data": {
"type": "email",
"recipients": ["admin@empresa.com"],
"subject": "Nuevo lead VIP",
"message": "El cliente {{contact.name}} está interesado en {{variables.producto}}"
}
}
webhook
Llama API externa.
{
"type": "webhook",
"id": "act_6",
"data": {
"url": "https://api.example.com/leads",
"method": "POST",
"headers": {
"Authorization": "Bearer {{secrets.api_key}}"
},
"body": {
"name": "{{contact.name}}",
"phone": "{{contact.phone}}",
"product": "{{variables.producto}}"
},
"output_variable": "api_response",
"retry": {
"max_attempts": 3,
"delay_seconds": 5
}
}
}
ai_response
Genera respuesta con IA.
{
"type": "ai_response",
"id": "act_7",
"data": {
"provider": "openai",
"model": "gpt-4",
"system_prompt": "Eres un asistente de ventas amable. Productos: {{odoo.products}}",
"include_context": {
"last_messages": 10,
"contact_info": true,
"odoo_data": true
},
"allowed_actions": ["transfer", "create_lead"],
"output_variable": "ai_response",
"fallback_on_error": "transfer"
}
}
Nodos de Odoo
Ver Integración Odoo para documentación completa.
Variables del Sistema
# Contacto
{{contact.id}}
{{contact.name}}
{{contact.phone}}
{{contact.email}}
{{contact.company}}
{{contact.tags}}
{{contact.odoo_id}}
{{contact.created_at}}
{{contact.custom.xxx}}
# Conversación
{{conversation.id}}
{{conversation.status}}
{{conversation.assigned_to}}
{{conversation.queue}}
{{conversation.started_at}}
# Mensaje actual
{{message.id}}
{{message.text}}
{{message.type}}
{{message.media_url}}
# Sistema
{{system.date}}
{{system.time}}
{{system.datetime}}
{{system.day_of_week}}
{{system.business_hours}}
{{system.timestamp}}
# Agente (si asignado)
{{agent.id}}
{{agent.name}}
{{agent.email}}
# Odoo (si conectado)
{{odoo.partner.id}}
{{odoo.partner.name}}
{{odoo.partner.email}}
{{odoo.partner.balance}}
{{odoo.last_order.name}}
{{odoo.last_order.total}}
{{odoo.last_order.state}}
# Variables capturadas
{{variables.xxx}}
Estructura de Flujo (JSON)
{
"id": "uuid",
"name": "Bienvenida",
"description": "Flujo de bienvenida",
"trigger_type": "welcome",
"trigger_value": null,
"nodes": {
"nodes": [
{
"id": "trigger_1",
"type": "welcome",
"position": { "x": 100, "y": 100 },
"data": { "label": "Inicio" }
},
{
"id": "msg_1",
"type": "text",
"position": { "x": 100, "y": 200 },
"data": { "content": "¡Hola!" }
}
],
"edges": [
{
"id": "e1",
"source": "trigger_1",
"target": "msg_1",
"sourceHandle": "default"
}
],
"viewport": {
"x": 0,
"y": 0,
"zoom": 1
}
},
"variables": {
"producto": { "type": "string", "default": "" },
"cantidad": { "type": "number", "default": 1 }
},
"is_active": true,
"version": 1
}
Mejores Prácticas
- Nombra los nodos claramente para fácil identificación
- Usa sub-flujos para lógica repetida (validaciones, etc.)
- Siempre incluye fallback para respuestas no entendidas
- Valida inputs antes de usarlos en acciones
- Limita reintentos para evitar loops infinitos
- Usa A/B testing para optimizar mensajes
- Transfiere a humano cuando el bot no puede resolver
- Guarda contexto para conversaciones que se retoman después