El motor de automatización de GMO
Antes de entender los flujos de MOVA hay que entender qué es n8n y por qué GMO lo eligió. Este capítulo no requiere conocimiento técnico.
La idea central
n8n es una herramienta de automatización de flujos de trabajo. La idea es simple: conectar aplicaciones y servicios para que hagan cosas automáticamente, sin que una persona tenga que estar en el medio coordinando.
Imagina que cada vez que llega una factura al email de GMO, alguien tiene que: abrir el email, descargar el PDF, revisar los datos, buscar si existe una OC para esa factura, registrar todo en una planilla y notificar al responsable. Eso es trabajo humano repetitivo. n8n hace exactamente eso, solo, las 24 horas del día, sin errores por cansancio.
Los flujos en n8n se construyen visualmente: arrastra bloques (llamados nodos), los conectas con flechas y defines qué hace cada uno. Un flujo puede ser tan simple como "cuando llegue un email, guárdalo en una planilla", o tan complejo como el M8 que procesa facturas con inteligencia artificial.
Por qué GMO usa n8n
n8n es el sistema nervioso de MOVA. Conecta el frontend HTML (lo que ve el equipo), con Claude (la inteligencia), con Google Workspace (los datos) y con servicios externos (SII, PDFs, APIs). Sin n8n, cada módulo MOVA sería una isla — con n8n, todos hablan entre sí.
| Qué hace n8n en GMO | Módulo MOVA relacionado |
|---|---|
| Recibe facturas por email y las procesa con Claude | M8 · Receptor de Egresos |
| Verifica 750 URLs de contenido SEO automáticamente | M15 · Auditor de Contenido |
| Autentica con el SII y descarga el RCV mensual | M7 · Conciliador SII |
| Recibe mensajes de AXON y los enruta al modelo correcto | AXON · Agente conversacional |
| Genera OCs y las registra en la BBDD de egresos | M2 · Generador de OC |
n8n vs las alternativas
| Criterio | n8n | Zapier | Make (Integromat) |
|---|---|---|---|
| Modelo de precio | Self-hosted gratis en droplet | Por tarea ejecutada — se dispara | Por operación — más razonable |
| Integración con Claude API | HTTP Request nativo, total control | Existe pero limitado | Existe pero menos flexible |
| Código en los flujos | Nodo Code con JS o Python completo | No permite código real | Limitado |
| Control sobre el servidor | Total — en nuestro propio droplet | Solo en la nube de Zapier | Solo en la nube de Make |
| Privacidad de datos | Los datos no salen del droplet GMO | Pasan por servidores de Zapier | Pasan por servidores de Make |
Entender un flujo sin construirlo
No necesitas saber programar para entender cómo funciona un flujo de n8n. Este capítulo te da el vocabulario para leerlos, discutirlos y proponer mejoras.
Anatomía de un flujo
Un flujo en n8n es una secuencia de pasos conectados. Cada paso se llama nodo. Los nodos se conectan de izquierda a derecha: el resultado de uno es la entrada del siguiente.
Todo flujo tiene tres partes:
- Trigger (inicio): Qué dispara el flujo. Puede ser un email nuevo, un webhook, una hora programada, o un clic en el frontend MOVA.
- Proceso (medio): Los pasos que transforman los datos. Aquí viven las llamadas a Claude, las validaciones y las transformaciones.
- Output (fin): Qué pasa con el resultado. Normalmente: escribir en Google Sheets, enviar una notificación, o devolver una respuesta al frontend.
Tipos de nodos
El patrón MOVA
Todos los módulos MOVA que usan Claude siguen el mismo esquema de 3 capas. Entender esto es entender la arquitectura completa de MOVA.
Qué automatiza n8n en GMO hoy
Cuatro flujos en producción o avanzado desarrollo. Para cada uno: qué hace, cómo funciona y qué aprendimos.
AXON · Agente conversacional
Webhook conversacional · Gemini + Claude · Asana
AXON recibe cada mensaje del equipo, decide qué modelo usar (Gemini para respuestas rápidas, Claude para análisis complejos) y puede crear tareas en Asana si detecta que el mensaje lo requiere.
Lección: El historial de conversación lo pasa el frontend en cada request — n8n solo lo recibe y lo incluye en el array de messages de la API. Claude ve toda la conversación en cada llamada.
M8 · Receptor de Egresos
IMAP · PDF base64 · Claude extracción · Validación vs OC · Google Sheets
Monitorea proveedores@mkof.cl, extrae datos de facturas PDF con Claude, las valida contra las órdenes de compra registradas y aprueba o rechaza automáticamente.
Lección: El nodo IF posterior al output de Claude es crítico. No asumas que Claude siempre devuelve JSON válido — valida antes de escribir en Sheets.
M15 · Auditor de Contenido
750 URLs · Loop · Scraping · Claude compara · Semáforo en Sheets
Lee una planilla con URLs y contenido esperado, scrapeea cada URL, le pide a Claude que compare y escribe el estado (✅⚠️❌🚫) en la misma planilla. 750 URLs en 15 minutos.
Lección: El Wait de 1.200ms es obligatorio. Sin él, superas los 50 requests/minuto de la API y el flujo empieza a fallar con error 429 a mitad del proceso.
M7 · Conciliador SII
Auth SOAP · Token SII · RCV mensual · PM2 en droplet
Autentica con el SII usando el certificado digital de GMO, descarga el RCV mensual y lo procesa para conciliación contable. Phase 1 completa (autenticación válida). Phase 2 (query RCV + categorización con Claude) en desarrollo.
Arquitectura especial: El servicio de autenticación SII corre como proceso Node.js independiente en PM2 en el droplet, no dentro de n8n. n8n llama a ese servicio vía HTTP local para obtener el token.
Lección: El certificado .pfx del SII expira en julio 2026. Antes de esa fecha hay que renovarlo con E-Certchile o el flujo completo deja de funcionar.
Los nodos que usa MOVA
Estos son los nodos que aparecen en prácticamente todos los flujos de GMO. Con dominar estos tienes el 90% de lo que necesitas para construir y mantener módulos MOVA.
Triggers
Webhook — el más usado en MOVA
Recibe una petición HTTP POST desde el frontend y dispara el flujo. Es la base del patrón MOVA.
// Configuración del nodo Webhook:
HTTP Method: POST
Path: /webhook/nombre-modulo ← ej: /webhook/m8-egresos
Response Mode: When Last Node Finishes ← devuelve resultado al frontend
// URL pública resultante:
https://grupomakingof.app.n8n.cloud/webhook/nombre-modulo
Schedule Trigger — para procesos periódicos
Dispara el flujo en un horario definido. Para M15 (auditoría semanal) o M7 (descarga mensual del SII).
// Ejemplos de expresión cron:
0 8 * * 1 ← Lunes 8:00 AM (M15)
0 2 1 * * ← Día 1 de cada mes a las 2AM (M7 SII)
0 7 * * 1-5 ← Lunes a viernes 7AM (briefing AXON)
Email Trigger (IMAP) — para M8
// Nodo: Email Trigger (IMAP)
Host: imap.gmail.com (o el de GoDaddy para mkof.cl)
Port: 993
Mailbox: INBOX
Action: Mark as read ← para no procesar 2 veces
HTTP Request
El nodo más versátil de n8n. Llama a cualquier API. En GMO lo usamos principalmente para Claude API y para servicios externos (SII, PDF.co).
// Configuración base para Claude API:
Method: POST
URL: https://api.anthropic.com/v1/messages
Authentication: Header Auth → credencial "Anthropic API"
Headers:
anthropic-version: 2023-06-01
Body (JSON):
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1000,
"system": "Tu system prompt",
"messages": [{
"role": "user",
"content": "={{ $json.input }}"
}]
}
Settings:
Retry On Fail: ON
Max Tries: 3
Wait Between Tries: 2000ms
Lógica e iteración
IF — para bifurcar según el resultado de Claude
// Ejemplo: validar que Claude devolvió JSON antes de parsear
Condición: {{ $json.content[0].text }} contains "{"
Branch TRUE → nodo JSON Parse → continúa el flujo
Branch FALSE → nodo Set (marca error) → notifica al equipo
Split in Batches + Loop — para M15 y otros batches
// Configuración para 750 URLs:
Batch Size: 1 ← procesa de a 1 para control total
Reset: false ← mantiene el estado entre iteraciones
// SIEMPRE agregar Wait node después del Split:
Wait: 1200ms ← = 50 requests/min máximo
Datos e integración
Google Sheets — la base de datos de MOVA
// Leer todas las filas activas:
Operation: Get Many Rows
Sheet ID: [ID del Sheet]
Filters: column "estado" equals "activo"
// Actualizar una fila específica por ID:
Operation: Update Row
Row Number: {{ $json.fila_id }}
Column: estado → "procesado"
Column: fecha_proceso → {{ $now.toISO() }}
Code node — para transformaciones complejas
// Extraer y parsear JSON de respuesta Claude:
const rawText = $input.first().json.content[0].text;
const clean = rawText.replace(/```json|```/g, '').trim();
try {
const parsed = JSON.parse(clean);
return [{ json: parsed }];
} catch(e) {
return [{ json: { error: 'JSON inválido', raw: rawText } }];
}
Patrones de integración Claude + n8n
Todo lo que necesitas para integrar Claude en cualquier flujo MOVA: desde la configuración base hasta el procesamiento de PDFs y el manejo de batches largos.
Configurar el nodo base
Antes de cualquier flujo que use Claude, crea la credencial en n8n:
- Settings → Credentials → New Credential → Header Auth
- Name: Anthropic API
- Header Name: x-api-key
- Header Value: sk-ant-XXXXXXXX
Una vez creada, la referencias en todos los nodos HTTP Request que llamen a Claude sin volver a pegar la key.
Extraer el texto de la respuesta
// En el nodo siguiente al HTTP Request:
{{ $json.content[0].text }}
// Tokens usados (para monitoreo de costo):
Input: {{ $json.usage.input_tokens }}
Output: {{ $json.usage.output_tokens }}
Pasar PDFs e imágenes
// Body JSON para PDF adjunto de email:
{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1000,
"system": "Extractor de facturas. Solo JSON válido.",
"messages": [{
"role": "user",
"content": [
{
"type": "document",
"source": {
"type": "base64",
"media_type": "application/pdf",
"data": "={{ $binary.data }}"
}
},
{
"type": "text",
"text": "Extrae los datos de esta factura."
}
]
}]
}
Para imágenes: cambia "document" por "image" y "application/pdf" por "image/jpeg" o "image/png".
Batch con Loop (patrón M15)
// Arquitectura completa de un flujo batch:
Google Sheets → [todas las filas]
↓
Split In Batches (size: 1)
↓
Wait (1200ms) ← OBLIGATORIO — rate limit
↓
HTTP Request → URL del ítem ← scraping
↓
Code → limpia el HTML, extrae texto
↓
HTTP Request → Claude API ← análisis
↓
IF → ¿JSON válido?
├── TRUE → Google Sheets Update (escribe resultado)
└── FALSE → Google Sheets Update (marca error)
↓
[vuelve al Loop]
↓
[fin] → Slack/Email notificación de completado
Parsear el output de Claude
El problema más frecuente en producción: Claude responde con texto envuelto en backticks de markdown. El JSON Parse de n8n falla.
// Code node después del HTTP Request a Claude:
const raw = $input.first().json.content[0].text;
// Limpiar backticks y espacios:
const clean = raw
.replace(/```json\n?/g, '')
.replace(/```\n?/g, '')
.trim();
// Intentar parsear con manejo de error:
try {
const data = JSON.parse(clean);
return [{ json: { ...data, _raw_ok: true } }];
} catch (e) {
// Si falla, devolver el texto crudo para debug:
return [{ json: { _raw_ok: false, _raw: clean, _error: e.message } }];
}
Luego usa un IF que verifica _raw_ok === true para bifurcar el flujo.
El servidor de GMO
La infraestructura técnica que sostiene MOVA: el droplet de DigitalOcean, cómo está organizado y las reglas para trabajar en producción sin romper cosas.
Infraestructura GMO
| Componente | Detalle | Para qué se usa |
|---|---|---|
| Droplet DigitalOcean | Ubuntu 22.04 · IP: 134.209.40.247 · nombre: n8n-mkof | n8n de producción, servicio de auth SII (PM2), base de Node.js services |
| n8n Cloud | grupomakingof.app.n8n.cloud | Desarrollo y pruebas. Los flujos que funcionan pasan al droplet. |
| GoDaddy cPanel | acme-chile.cl | Frontend HTML de los módulos MOVA, público en /public_html |
| MySQL GoDaddy | Host: p3plzcpnl508505.prod.phx3.secureserver.net | Auth MOVA (sistema PHP/MySQL en desarrollo) |
| Cert SII | .pfx · E-Certchile · expira jul 2026 | Autenticación con SII para M7 |
Regla de los dos entornos
Siempre desarrolla en n8n Cloud primero. Cuando un flujo está probado y estable, se migra al droplet de producción. Nunca experimentes directamente en el droplet — si el flujo falla en producción afecta módulos reales que el equipo está usando.
Errores comunes y solución
| Error | Causa más probable | Solución |
|---|---|---|
| 401 Unauthorized | API key de Claude incorrecta o expirada en las credenciales de n8n | Settings → Credentials → Anthropic API → verificar la key |
| 429 Rate Limit | Demasiados requests por minuto (más de 50) | Agregar Wait de 1200ms antes del HTTP Request a Claude |
| JSON Parse failed | Claude envolvió el JSON en backticks de markdown | Usar el Code node de limpieza antes del JSON Parse |
| Webhook timeout | El flujo tarda más de 30s y el frontend pierde la conexión | Cambiar Response Mode a "Immediately" y devolver el resultado por otro canal (Sheets + polling) |
| IMAP connection error | Contraseña de email cambiada o 2FA activado | Actualizar credencial IMAP en n8n con app password |
| Flujo se ejecuta 2 veces | Webhook recibe el mismo request dos veces (frontend con retry) | Agregar nodo deduplicación con ID único del request |
Seguridad y credenciales
- API keys siempre en Credentials de n8n — nunca en el body JSON de un nodo, nunca en variables de texto plano, nunca en el código de un nodo Code.
- Webhooks con shared secret — para webhooks que reciben datos desde el frontend MOVA, agrega un header secreto que n8n verifica antes de procesar. Evita que cualquier persona pueda llamar al webhook si conoce la URL.
- Variables de entorno en el droplet — las credenciales más sensibles (cert SII, DB password) van en variables de entorno del sistema, no dentro de n8n.
- Google OAuth restringido a @mkof.cl — las integraciones con Google Workspace solo permiten autenticación con cuentas del dominio GMO.
// Shared secret en webhook MOVA:
// En el nodo Webhook de n8n, agregar validación:
Header: X-MOVA-Secret
Value: {{ $env.MOVA_WEBHOOK_SECRET }}
// En el frontend HTML:
headers: {
'Content-Type': 'application/json',
'X-MOVA-Secret': '[valor del secret]'
}
Cheatsheet
Todo lo que necesitas a mano.
URLs y endpoints GMO
Snippets de código
Fetch desde frontend al webhook n8n
async function llamarMOVA(endpoint, datos) {
const res = await fetch(
`https://grupomakingof.app.n8n.cloud/webhook/${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-MOVA-Secret': '[secret]'
},
body: JSON.stringify(datos)
});
return await res.json();
}
Limpiar y parsear output de Claude
const raw = $input.first().json.content[0].text;
const clean = raw.replace(/```json\n?|```\n?/g, '').trim();
try {
return [{ json: JSON.parse(clean) }];
} catch(e) {
return [{ json: { _error: true, _raw: clean } }];
}
Google Sheets: update de fila por número
// Nodo Google Sheets:
Operation: Update Row
Spreadsheet: [ID del Sheet]
Sheet: BBDD Egresos
Row Number: {{ $json.fila_numero }}
Columns: estado = "procesado", fecha = {{ $now.toFormat('yyyy-MM-dd') }}
Tabla de errores rápida
| Código | Qué significa | Fix inmediato |
|---|---|---|
| 401 | API key inválida | Verificar credencial Anthropic en n8n Settings |
| 400 | Body JSON malformado | Probar el prompt en console.anthropic.com/workbench primero |
| 429 | Rate limit superado | Agregar Wait 1200ms antes del HTTP Request |
| 529 | Servidores Anthropic saturados | Retry automático lo resuelve; si persiste, esperar 5 min |
| ECONNRESET | Timeout de red en el droplet | Reiniciar el proceso n8n en PM2: pm2 restart n8n |
| JSON Parse error | Claude devolvió texto con backticks | Usar el Code node de limpieza antes del JSON Parse |