§ Guía técnica · how-to 18 · abril · 2026 12 min · nivel intermedio stack: n8n · GPT-4o · Holded

Cómo automatizar
la facturación
con n8n + GPT-4o.

Guía práctica con código real. Al final de la lectura tienes un workflow que recibe facturas por email, extrae los datos con IA, valida contra tus órdenes de compra y registra en tu ERP. Sin teclear.

TL;DR

Un workflow n8n que escucha una casilla facturas@, envía cada PDF a GPT-4o para extracción estructurada, valida el CIF contra tu maestro de proveedores, cruza el importe con órdenes de compra abiertas y crea el registro en Holded/Odoo/Sage. Tiempo por factura: ~8 segundos. Sustituye 4–7 minutos de trabajo manual por factura. Este workflow está corriendo hoy en clientes reales procesando +8.000 facturas/mes.

Pre-requisito honesto Necesitas a alguien con perfil técnico para implementarlo bien. Si no tienes a nadie en casa, te lo hacemos nosotros con fee cerrado: 4 semanas de construcción + 4 de estabilización supervisada antes de dejarlo 100 % autónomo.

Prerequisitos

  • n8n 1.40+ self-hosted (Docker o Node). Versión Cloud también funciona pero con coste por ejecución.
  • OpenAI API key con acceso a GPT-4o (modelo gpt-4o-2024-08-06 o superior).
  • Casilla de email dedicada con IMAP activo (ej. [email protected]).
  • ERP con API. Holded, Odoo, Sage 200, Business Central — todos funcionan. Si tu ERP no tiene API oficial, miramos RPA.
  • PostgreSQL para el log de procesamiento (o SQLite si eres muy espartano).

Paso 01Preparar el entorno n8n

Si aún no tienes n8n corriendo, la forma más rápida es Docker Compose. Este compose mínimo te deja n8n + Postgres corriendo en un VPS de 35€/mes:

# docker-compose.yml
services:
  n8n:
    image: n8nio/n8n:1.40
    restart: always
    ports: ["5678:5678"]
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - N8N_ENCRYPTION_KEY=${N8N_KEY}
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.tuempresa.com
    volumes: ["./data:/home/node/.n8n"]
  postgres:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=${PG_PASS}
    volumes: ["./pg:/var/lib/postgresql/data"]

Arranca con docker compose up -d, apunta un subdominio, pon un Caddy o Traefik delante para TLS, y entra a la UI para configurar credenciales.

Screenshot · panel de credenciales en n8n (IMAP + OpenAI + Holded)

Paso 02Configurar el trigger IMAP

Añade un nodo Email Trigger (IMAP) al canvas. Configura:

  • Host: el IMAP de tu proveedor (ej. imap.gmail.com, outlook.office365.com).
  • Mailbox: INBOX o una carpeta específica si filtras por reglas.
  • Post-process: mover a carpeta Procesadas para no reprocesar.
  • Download attachments: activado, filtrado a application/pdf.
Tip No uses tu email principal. Crea facturas@ dedicado y da la dirección a tus proveedores. Así aíslas el flow de ruido y puedes borrar todo el histórico sin afectar otras bandejas.

Paso 03Extraer datos con GPT-4o Vision

Conecta el trigger a un nodo OpenAI · Message a Model. Modelo gpt-4o, modo JSON. El prompt clave:

// Prompt de extracción estructurada
Eres un extractor de datos de facturas. Devuelve SIEMPRE JSON
válido con esta forma:

{
  "cif_proveedor": string,
  "nombre_proveedor": string,
  "numero_factura": string,
  "fecha_emision": string (ISO 8601),
  "base_imponible": number,
  "iva": number,
  "total": number,
  "moneda": string (ISO 4217),
  "lineas": [{ descripcion, cantidad, precio_unitario, total }],
  "confidence": number (0–1)
}

Si un campo no está claro, pon null y baja confidence.
No inventes datos. No añadas comentarios. Solo JSON.

Pasa el PDF como input de imagen (GPT-4o lo convierte internamente). Para PDFs de más de 3 páginas, divide y procesa por página; luego fusiona.

Screenshot · nodo OpenAI con prompt y output JSON expandido

Paso 04Validar contra proveedores y órdenes

Añade un nodo Code (JavaScript) después del OpenAI para validar. Lógica mínima:

const factura = $json;
const errores = [];

// 1. CIF debe existir en maestro de proveedores
const proveedor = await $lookup('proveedores', { cif: factura.cif_proveedor });
if (!proveedor) errores.push('proveedor_desconocido');

// 2. Importe debe cuadrar con alguna orden abierta (±1%)
const ordenes = await $lookup('ordenes_compra', {
  proveedor_cif: factura.cif_proveedor,
  estado: 'abierta'
});
const match = ordenes.find(o =>
  Math.abs(o.total - factura.total) / o.total < 0.01
);
if (!match) errores.push('sin_orden_coincidente');

// 3. Confidence mínimo
if (factura.confidence < 0.85) errores.push('confidence_baja');

return { factura, orden: match, errores };

Usa un nodo IF después para bifurcar: si errores.length === 0, sigue al registro automático; si no, escala a humano (paso 6).

Paso 05Registrar en el ERP

Aquí cambia el nodo según tu ERP. Ejemplos reales:

  • Holded: nodo HTTP Request a POST /invoicing/v1/documents/purchase con el JSON mapeado.
  • Odoo: nodo Odoo nativo, modelo account.move, type in_invoice.
  • Sage 200: API REST vía HTTP Request + API key de usuario técnico.

Después del registro, sube el PDF original como adjunto al documento creado. Todos los ERPs modernos tienen endpoint /attachments para esto.

Importante Siempre guarda el external_id que devuelve el ERP en tu Postgres local. Así puedes reconciliar y volver a buscar si algo falla.

Paso 06Escalar excepciones a humano

La rama de error del IF conecta a un nodo Slack (o Teams, Email, lo que uses). El mensaje debe incluir:

  • Qué validación falló (errores.join(', ')).
  • Resumen de la factura (proveedor, número, total).
  • Link al PDF original guardado en S3 o tu file server.
  • Botón "Aprobar manualmente" que dispara un webhook de vuelta a n8n para forzar el registro.
Screenshot · mensaje Slack con resumen de factura y botones de acción

Errores comunes (y cómo evitarlos)

  • PDFs escaneados con mala calidad → GPT-4o baja confidence. Solución: pasar por un preprocess de imagen (nodo Image con aumento de contraste) antes del OpenAI.
  • Facturas con moneda distinta al EUR → olvidas añadir conversión. Solución: nodo de tipo de cambio vía exchangerate.host antes de registrar.
  • Proveedores que mandan un ZIP con varias facturas → el trigger solo coge el primer PDF. Solución: nodo Extract from ZIP antes del OpenAI.
  • Timeout en facturas largas → aumenta el timeout del nodo OpenAI a 120s y divide PDFs de +5 páginas.
  • Rate limit de OpenAI en picos de final de mes → activa retry con backoff exponencial (3 reintentos, base 2s).

Qué hacer después

Este workflow es la base. Una vez funcionando, las extensiones naturales son:

  1. Conciliación bancaria: un segundo flow que cruza pagos bancarios con estas facturas registradas. Guía →
  2. Previsión de tesorería: dashboard Grafana con vencimientos proyectados a 30/60/90. Guía →
  3. Detección de fraude: modelo que marca facturas con patrones anómalos (cambio súbito de IBAN del proveedor, importes fuera de rango histórico).

Si prefieres no construirlo tú, hablamos 30 minutos y lo dejamos corriendo en tu infra: 4 semanas de construcción + 4 de estabilización supervisada antes de pasar a producción 100 % autónoma. Fee cerrado.

§ Continúa leyendo

Más guías de n8n.

Comparativa · pilar

n8n vs Zapier vs Make 2026

TCO real, RGPD, rendimiento. Cuál elegir según tu tamaño.

How-to · próximamente

Conciliación bancaria PSD2 con n8n

Lee movimientos de 3 bancos, cruza con facturas, concilia solo.

How-to · próximamente

Self-host n8n en un VPS con Docker

VPS, Caddy, backups, monitoring. Setup completo en 30 minutos.

¿Te ahorro el fin de semana y te lo dejo corriendo?

Construimos este workflow (y los siguientes) en tu infra: 4 semanas de construcción + 4 de estabilización supervisada antes de pasar a producción autónoma. Fee cerrado. Cero coste por ejecución.

Solicitar diagnóstico →