Módulo 7: Wait Nodes y Flujos Asíncronos

Human-in-the-Loop (intervención humana)

Descripción de la cápsula

Algunos workflows no deberían automatizarse 100%: requieren juicio humano. Ejemplos: aprobar refunds grandes, validar contenido antes de publicar, decidir entre opciones complejas. Human-in-the-loop (HITL) es el patrón que pausa el workflow para que un humano decida, después continúa.

En esta cápsula vas a aprender los 3 patterns de HITL (Slack actionable, email con links, formularios), cuándo aplicar, y cómo evitar las trampas.


Lo que vas a aprender

  • Diseñar HITL efectivos
  • 3 patterns: Slack interactivo, email con links, form
  • Manejar timeouts y respuestas no recibidas
  • Trade-offs vs automatización completa

El concepto

[Procesamiento automático]
   │
[Wait for human decision]
   │
[Procesamiento continúa según decisión humana]

El "Wait for human" usa Wait for Webhook (cápsula 05). El humano interactúa con algún medio (Slack, email, form) que dispara el resume.


Pattern 1: Slack interactivo

Slack tiene Block Kit con botones que pueden disparar webhooks.

Implementación simplificada

[Trigger]
   │
[Slack: send message con botones]
  Blocks con buttons que tienen URLs:
  - Approve: $resumeUrl?action=approve
  - Reject: $resumeUrl?action=reject
   │
[Wait for Webhook: 24h timeout]
   │
[Switch: $json.body.action]
├─→ "approve" → procesar
├─→ "reject" → notificar y descartar
└─→ timeout → escalar

Block Kit con buttons que abren URL

[
  {
    "type": "section",
    "text": { "type": "mrkdwn", "text": "¿Aprobar refund de ${{ $json.amount }}?" }
  },
  {
    "type": "actions",
    "elements": [
      {
        "type": "button",
        "text": { "type": "plain_text", "text": "✅ Aprobar" },
        "url": "{{ $resumeUrl }}?action=approve",
        "style": "primary"
      },
      {
        "type": "button",
        "text": { "type": "plain_text", "text": "❌ Rechazar" },
        "url": "{{ $resumeUrl }}?action=reject",
        "style": "danger"
      }
    ]
  }
]

Click en botón → GET request al URL → reanuda workflow.


Pattern 2: Email con links

Similar pero por email.

[Send Email]
  HTML:
  <p>Decidir sobre refund de ${{ $json.amount }}:</p>
  <a href="{{ $resumeUrl }}?action=approve">✅ Aprobar</a> |
  <a href="{{ $resumeUrl }}?action=reject">❌ Rechazar</a>
   │
[Wait for Webhook]
   │
[Switch action]

Pros

  • Funciona sin instalar nada
  • Email es más async-friendly (admin responde cuando puede)

Contras

  • Click accidental difícil de revertir
  • Sin contexto rich (vs Slack threads)

Pattern 3: Form de aprobación

Para decisiones que requieren input adicional del humano (no solo botón):

[Send Email/Slack con link a form]
  El link incluye execution_id que identifica este workflow
   │
[Wait for Webhook]

El form (Tally, Typeform) tiene campos:

  • Action: approve/reject
  • Comentarios
  • Otros campos relevantes

Cuando el humano submit el form, su webhook resume el workflow con todos los datos.


Cuándo usar HITL

  • Decisiones con consecuencias significativas
  • Casos edge que no son frecuentes (no vale automatizar)
  • Compliance requiere human review
  • Validación final antes de acción irreversible

No

  • Decisiones de alto volumen (cuello de botella)
  • Decisiones simples que pueden automatizarse
  • Cuando humanos no están disponibles 24/7

Diseño defensivo de HITL

Timeout explícito

Humanos olvidan. Olvidan responder en vacaciones. Siempre timeout:

[Wait for Webhook: 24h timeout]
   │
[IF: timeout?]
├─ TRUE → [escalación: notificar manager + fallback]
└─ FALSE → procesar decisión

Idempotencia (botones)

Si humano hace click 2 veces, ¿pasa algo?

  • Primer click: workflow reanuda
  • Segundo click: URL ya inválida (n8n no reanuda 2x)

Pero si quieres confirmación al humano del primer click, el resume URL podría devolver una página HTML "Aprobado, gracias".

Auditoría

Loggear cada decisión:

[Set: log decision]
- decided_by: extraído del webhook si está disponible
- decided_at: $now
- decision: action
   │
[Sheet append 'decisions_log']

Multi-aprobador

A veces necesitas N aprobadores: "monto > $5000 requiere 2 aprobaciones".

[Wait for Webhook 1] → first approval
   │
[Send to approver 2]
   │
[Wait for Webhook 2] → second approval
   │
[procesar]

Cada aprobación pausa workflow.


Trade-offs vs automatización

Automatización completa

Pros: rápida, escala, sin cuello de botella. Contras: errores caros, sin contexto humano para edge cases.

HITL

Pros: control humano, edge cases manejados. Contras: cuello de botella, depende de disponibilidad humana.

Híbrido recomendado

[Reglas automáticas para casos comunes]
   │
[IF: caso normal]
├─ TRUE → automatizado
└─ FALSE → [HITL: human decide]

90% automático, 10% HITL para casos no obvios.


Trampas comunes

Trampa 1: HITL sin timeout

Qué pasa: Aprobador renuncia, va de vacaciones, ignora email. Workflow espera para siempre.

Cómo evitar: Timeout + escalación.


Trampa 2: Botones sin confirmación

Qué pasa: Botón "Reject" en email. Click accidental. Sin forma de deshacer.

Cómo evitar: Botones llevan a página de confirmación antes del POST final.


Trampa 3: Resume URL filtrada en archives

Qué pasa: Email con resume URL queda en archive accesible públicamente. Alguien externo "aprueba".

Cómo evitar: Auth en el resume webhook + verificar identidad del clicker (cookie, header, etc.).


Trampa 4: Demasiado HITL

Qué pasa: Pides aprobación humana para cada cosa. Equipo abrumado. Decisiones tardan días.

Cómo evitar: Reglas claras de cuándo HITL: solo casos donde realmente vale la pena.


Resumen

  • HITL: pausa workflow para que humano decida
  • 3 patterns: Slack interactivo, email con links, form
  • Cuándo usar: decisiones críticas, baja frecuencia, edge cases
  • Defensa: timeout, idempotencia, auditoría, multi-aprobador
  • Híbrido: automático para común, HITL para edge cases
  • 4 trampas: sin timeout, botones sin confirm, URL filtrada, demasiado HITL

Lo que sigue: Long-running workflows — consideraciones para flows de duración extensa.


Creado: Mayo 11, 2026 Versión: 1.0