n8n Webhook Tutorial: Guía Completa 2026

El nodo Webhook es el disparador más potente y versátil en n8n. Transforma n8n en un punto final de API en tiempo real al que cualquier sistema externo puede llamar: Stripe, GitHub, Typeform, tu propia aplicación o cualquier servicio que pueda realizar solicitudes HTTP.

En este tutorial completo, aprenderás todo sobre los webhooks en n8n: qué son, cómo crearlos y configurarlos, la diferencia entre los modos de prueba y producción, las opciones de autenticación, cómo procesar los datos entrantes con expresiones y cómo enviar datos a servicios externos con el nodo HTTP Request.

📋 Qué Aprenderás

Cómo crear un disparador Webhook · Modo de prueba vs producción · Autenticación de webhooks · Procesamiento de datos de webhook · Respuesta a webhooks · Integración de webhook de Stripe · Integración de webhook de GitHub · Envío de solicitudes HTTP desde n8n · Consejos comunes para la resolución de problemas

¿Qué Son los Webhooks? Una Introducción Rápida

Los webhooks son un mecanismo para que una aplicación notifique a otra cuando sucede algo, utilizando HTTP. En lugar de que tu aplicación pregunte continuamente "¿cambió algo?" (polling), el servicio externo te envía datos en el momento en que ocurre un evento.

Piensa en los webhooks como "APIs inversas":

  • Llamada API tradicional: Tu aplicación → pregunta → Servicio externo → responde
  • Webhook: Servicio externo → envía datos → Tu punto final (n8n)

Ejemplos comunes del mundo real:

  • Stripe: Envía un webhook cuando un pago tiene éxito, la suscripción se renueva o se emite un reembolso
  • GitHub: Envía un webhook en push, pull request, creación de issue o despliegue
  • Typeform / JotForm: Envía datos de respuesta del formulario cuando alguien envía un formulario
  • Shopify: Envía datos de pedido cuando se completa una nueva compra
  • Tu propia aplicación: Puede enviar webhooks para activar flujos de trabajo de n8n desde cualquier acción del usuario

Creación de Tu Primer Disparador Webhook en n8n

Configurar un disparador Webhook en n8n lleva menos de 2 minutos. Aquí está el proceso paso a paso:

1

Crear un Nuevo Flujo de Trabajo

Abre n8n, haz clic en Nuevo Flujo de Trabajo en la parte superior derecha. El lienzo del flujo de trabajo se abrirá con un estado vacío.

2

Añadir el Nodo Webhook

Haz clic en el botón + (o presiona Tab) para abrir el menú del nodo. Busca "Webhook" y selecciona el nodo disparador Webhook. Aparecerá como el primer nodo en tu flujo de trabajo.

3

Configurar el Webhook

Haz clic en el nodo Webhook para abrir su panel de configuración. Configura:

  • Método HTTP: POST (para recibir datos), GET (para disparadores simples) o cualquier otro método
  • Ruta: Una ruta URL única (por ejemplo, mi-flujo-de-trabajo o eventos-stripe)
  • Autenticación: Ninguna por ahora (cubriremos la autenticación más adelante)
  • Responder: Inmediatamente (devuelve 200 OK de inmediato) o Cuando el Último Nodo Termina
4

Copiar Tu URL de Webhook

n8n genera dos URLs después de la configuración. Las verás en el panel del nodo:

URL de Prueba (solo activa en el editor con "Escuchar Evento de Prueba")
https://your-n8n.domain.com/webhook-test/mi-flujo-de-trabajo
URL de Producción (siempre activa cuando el flujo de trabajo está habilitado)
https://your-n8n.domain.com/webhook/mi-flujo-de-trabajo
5

Probar el Webhook

Haz clic en "Escuchar Evento de Prueba" en el nodo. Luego envía una solicitud de prueba a la URL de prueba. n8n capturará los datos entrantes y los mostrará en el panel de salida del nodo.

Modo de Prueba vs Modo de Producción

Comprender los dos modos de webhook es fundamental: mezclarlos es uno de los errores más comunes de n8n.

Característica URL de Prueba (/webhook-test/) URL de Producción (/webhook/)
Cuándo Activo Solo cuando haces clic en "Escuchar Evento de Prueba" en el editor Siempre activo cuando el flujo de trabajo está habilitado
Ejecución Guardada No: muestra los datos en el editor pero no los guarda Sí: registro de ejecución completo en el Historial
Tiempo de Espera 120 segundos (espera la solicitud y luego se cierra automáticamente) Sin tiempo de espera: siempre escuchando
Solicitudes Simultáneas Solo una a la vez Se admiten múltiples solicitudes concurrentes
Usar Para Solo desarrollo y pruebas Tráfico de producción real
⚠️ Crítico: Nunca Uses la URL de Prueba en Producción

La URL de prueba solo funciona cuando tienes el flujo de trabajo abierto en el editor y has hecho clic en "Escuchar Evento de Prueba". Si utilizas la URL de prueba como un punto final de webhook en Stripe, GitHub o cualquier servicio externo, sus webhooks fallarán el 99% de las veces (cuando no estés probando activamente). Siempre utiliza la URL de producción para integraciones reales.

Autenticación de Webhook

Sin autenticación, cualquiera que descubra tu URL de webhook puede enviar solicitudes y activar tu flujo de trabajo. n8n ofrece varios métodos de autenticación:

Opción 1: Autenticación de Cabecera

El enfoque más común. El servicio externo envía una clave secreta en una cabecera HTTP específica. n8n valida el valor de la cabecera antes de ejecutar el flujo de trabajo.

# In n8n Webhook node settings: Authentication: Header Auth Credential Name: My Webhook Secret Header Name: X-Webhook-Secret Header Value: my-super-secret-key-here # The external service must include this header: curl -X POST https://n8n.yourdomain.com/webhook/my-workflow \ -H "X-Webhook-Secret: my-super-secret-key-here" \ -H "Content-Type: application/json" \ -d '{"event": "payment.success", "amount": 99.99}'

Opción 2: Autenticación Básica

Utiliza la autenticación básica HTTP estándar con nombre de usuario y contraseña. Las credenciales se envían en la cabecera de Autorización como "nombre de usuario:contraseña" codificado en base64.

# In n8n Webhook node settings: Authentication: Basic Auth Username: n8n-webhook Password: your-secure-password # Calling with Basic Auth: curl -X POST https://n8n.yourdomain.com/webhook/my-workflow \ -u "n8n-webhook:your-secure-password" \ -H "Content-Type: application/json" \ -d '{"data": "value"}'

Opción 3: Verificación de Firma (Patrón Stripe, GitHub)

Muchos servicios como Stripe y GitHub firman sus cargas útiles de webhook con HMAC-SHA256. Verificas la firma para confirmar que la solicitud es genuina. Esto requiere el nodo Code:

// Code node: Verify Stripe webhook signature const crypto = require('crypto'); const payload = $input.first().json.body; const signature = $input.first().json.headers['stripe-signature']; const secret = 'whsec_your_stripe_webhook_secret'; // Parse the Stripe signature header const sigParts = signature.split(',').reduce((acc, part) => { const [key, val] = part.split('='); acc[key] = val; return acc; }, {}); const timestamp = sigParts['t']; const payloadString = JSON.stringify(payload); const signedPayload = `${timestamp}.${payloadString}`; const expectedSig = crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex'); if (`v1=${expectedSig}` !== sigParts['v1']) { throw new Error('Invalid webhook signature — request rejected'); } return [{ json: payload }];

Procesamiento de Datos de Webhook con Expresiones

Cuando llega una solicitud de webhook, n8n estructura los datos entrantes con estos campos de nivel superior:

// n8n webhook data structure { "body": { ... }, // POST body (parsed JSON or form data) "headers": { ... }, // All request headers (lowercase keys) "params": { ... }, // URL path parameters (/:param) "query": { ... } // Query string parameters (?key=value) } // Example: Stripe payment webhook body { "body": { "type": "payment_intent.succeeded", "data": { "object": { "id": "pi_3OXv...", "amount": 9999, "currency": "usd", "receipt_email": "customer@email.com" } } } }

Accede a estos datos en nodos posteriores utilizando expresiones n8n:

// Accessing webhook data in expressions {{ $json.body.type }} // "payment_intent.succeeded" {{ $json.body.data.object.id }} // "pi_3OXv..." {{ $json.body.data.object.amount / 100 }} // 99.99 (cents to dollars) {{ $json.body.data.object.receipt_email }} // "customer@email.com" {{ $json.headers["content-type"] }} // "application/json" {{ $json.headers["x-custom-header"] }} // Custom header value {{ $json.query.page }} // ?page=2 query param {{ $json.query.filter }} // ?filter=active query param

Respondiendo a Webhooks

Muchos servicios esperan una respuesta específica a sus webhooks. n8n ofrece tres modos de respuesta:

Modo 1: Responder Inmediatamente

n8n devuelve HTTP 200 tan pronto como se recibe el webhook, sin esperar a que termine el flujo de trabajo. Utiliza esto para escenarios de "disparar y olvidar" donde el servicio de envío no necesita una respuesta.

Modo 2: Cuando el Último Nodo Termina

n8n espera a que se complete todo el flujo de trabajo, luego devuelve la salida del último nodo como el cuerpo de la respuesta. Utiliza esto cuando la persona que llama espera que se devuelvan datos (por ejemplo, un patrón de llamada API síncrona).

Modo 3: Responder al Nodo Webhook

El modo más flexible. Coloca el nodo Responder a Webhook en cualquier lugar de tu flujo de trabajo para enviar una respuesta personalizada en cualquier momento, luego continúa el flujo de trabajo de forma asíncrona.

// Respond to Webhook node configuration Respond With: JSON Response Code: 200 Response Body: { "success": true, "message": "Webhook received", "id": "{{ $json.body.id }}", "processedAt": "{{ $now.toISO() }}" }
💡 Stripe Requiere 200 Dentro de 30 Segundos

Stripe marca los webhooks como fallidos si no reciben una respuesta 2xx dentro de los 30 segundos. Utiliza Responder Inmediatamente o el nodo Responder a Webhook al principio de tu flujo de trabajo, luego continúa procesando de forma asíncrona. Esto evita que Stripe reintente e inunde tu flujo de trabajo.

Ejemplo Real: Procesamiento de Webhooks de Stripe

Construyamos un flujo de trabajo completo de webhook de pago de Stripe que:

  1. Recibe un evento payment_intent.succeeded de Stripe
  2. Responde inmediatamente con 200 (para evitar reintentos de Stripe)
  3. Extrae los detalles del pago
  4. Guarda el registro en Google Sheets
  5. Envía un correo electrónico de confirmación al cliente
  6. Notifica al equipo en Slack
1

Nodo Webhook (POST, ruta: stripe-events)

Método HTTP: POST | Autenticación: Ninguna (verificamos la firma en el código) | Responder: Usando el Nodo 'Responder a Webhook'

2

Nodo Code: Verificar la Firma de Stripe

Valida la firma del webhook de Stripe usando crypto (ver el código arriba). Lanza un error para firmas inválidas, lo que detiene el flujo de trabajo.

3

Nodo IF: Filtrar Tipos de Evento

Condición: {{ $json.type }} es igual a payment_intent.succeeded
La rama True continúa; la rama False termina silenciosamente.

4

Nodo Responder a Webhook

Devuelve inmediatamente {"received": true} con HTTP 200. Stripe recibe la confirmación. El flujo de trabajo continúa de forma asíncrona.

5

Nodo Set Fields: Extraer Datos de Pago

paymentId: {{ $json.data.object.id }} amount: {{ $json.data.object.amount / 100 }} currency: {{ $json.data.object.currency.toUpperCase() }} email: {{ $json.data.object.receipt_email }} name: {{ $json.data.object.billing_details.name }} timestamp: {{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }}
6

Paralelo: Google Sheets + Gmail + Slack

Tres nodos se ejecutan en paralelo desde la salida de Set Fields:

  • Google Sheets: Añadir fila a la hoja "Pagos"
  • Gmail: Enviar recibo HTML a {{ $json.email }}
  • Slack: Publicar en el canal #revenue: "💰 Nuevo pago: ${{ $json.amount }} de {{ $json.name }}"

Ejemplo Real: Webhook de GitHub para Notificaciones de CI/CD

Este flujo de trabajo recibe eventos push de GitHub y envía notificaciones al equipo:

// GitHub webhook payload (push event) { "ref": "refs/heads/main", "repository": { "name": "my-project", "full_name": "org/my-project" }, "pusher": { "name": "john.doe" }, "commits": [ { "id": "abc123", "message": "fix: resolve payment processing bug", "url": "https://github.com/org/my-project/commit/abc123" } ], "head_commit": { "added": ["src/utils.ts"], "modified": ["src/payments.ts", "tests/payments.test.ts"], "removed": [] } }

Configura GitHub para enviar webhooks a tu URL de n8n:

  1. Ve a tu repositorio de GitHub → Configuración → Webhooks → Añadir webhook
  2. Establece la URL de Payload a tu URL de webhook de producción de n8n
  3. Tipo de contenido: application/json
  4. Secreto: genera un secreto aleatorio y añádelo a la credencial de Autenticación de Cabecera de n8n
  5. Selecciona eventos: Push, Pull Request o eventos específicos

Expresiones n8n para Datos de GitHub

// Access GitHub webhook data {{ $json.body.repository.name }} // "my-project" {{ $json.body.pusher.name }} // "john.doe" {{ $json.body.ref.replace('refs/heads/', '') }} // "main" (branch name) {{ $json.body.commits.length }} // 1 (number of commits) {{ $json.body.head_commit.message }} // "fix: resolve payment..." {{ $json.body.commits[0].url }} // Commit URL // Format modified files list {{ $json.body.head_commit.modified.join(', ') }}

Envío de Datos: Nodo HTTP Request

La otra cara de la recepción de webhooks es enviarlos: llamar a APIs y webhooks externos desde tu flujo de trabajo de n8n. El nodo HTTP Request maneja todas las llamadas HTTP salientes.

Solicitud POST Básica

// HTTP Request node config: POST JSON to an API Method: POST URL: https://api.yourservice.com/events Auth: Header Auth → Authorization: Bearer {{ $credentials.apiToken }} Headers: Content-Type: application/json Body: { "event": "user_signup", "userId": "{{ $json.userId }}", "email": "{{ $json.email }}", "timestamp": "{{ $now.toISO() }}" }

Solicitud GET con Parámetros de Consulta

// HTTP Request: GET with query params Method: GET URL: https://api.weatherapi.com/v1/current.json Query Params: key={{ $credentials.weatherApiKey }} q={{ $json.city }} aqi=no // Resulting URL: https://api.weatherapi.com/v1/current.json?key=...&q=London&aqi=no

Manejo de la Paginación de la API

Muchas APIs paginan sus respuestas. El nodo HTTP Request tiene soporte de paginación incorporado:

Pagination Mode: Response Contains Next URL // For APIs with cursor-based pagination: Next URL Expression: {{ $response.body.meta.next_cursor ? 'https://api.example.com/users?cursor=' + $response.body.meta.next_cursor : '' }} // For APIs with page number pagination: Pagination Mode: Update a Parameter in Each Request Pagination Key: page Initial Value: 1 Increment By: 1 Complete When: {{ $response.body.data.length === 0 }}

Caso de Uso de Webhook: Envío de Formularios

El procesamiento de envíos de formularios es uno de los casos de uso de webhook más comunes. Aquí te mostramos cómo manejar formularios de Typeform, HubSpot y formularios HTML personalizados:

Webhook de Typeform

Configurar en Typeform: Conectar → Webhooks → añadir tu URL de n8n.

// Typeform webhook data structure { "event_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV", "event_type": "form_response", "form_response": { "answers": [ { "field": { "ref": "name" }, "text": "John Smith" }, { "field": { "ref": "email" }, "email": "john@example.com" }, { "field": { "ref": "company" }, "text": "Acme Corp" } ] } } // n8n expressions to extract Typeform answers {{ $json.body.form_response.answers.find(a => a.field.ref === 'name')?.text }} {{ $json.body.form_response.answers.find(a => a.field.ref === 'email')?.email }}

Formulario HTML Personalizado → Webhook de n8n

Puedes publicar directamente desde cualquier formulario HTML a un webhook de n8n usando JavaScript:

// Frontend: Post form data to n8n webhook document.querySelector('#myForm').addEventListener('submit', async (e) => { e.preventDefault(); const formData = Object.fromEntries(new FormData(e.target)); const response = await fetch( 'https://n8n.yourdomain.com/webhook/form-submissions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...formData, submittedAt: new Date().toISOString(), source: document.referrer || window.location.href }) } ); const result = await response.json(); console.log('Webhook response:', result); });

Problemas Comunes de Webhook y Soluciones

Problema 1: Webhook No Recibe Datos

  • Verificar: ¿Estás utilizando la URL de producción (no la URL de prueba) y está habilitado el flujo de trabajo?
  • Verificar: ¿Es accesible n8n desde Internet? (Las instancias auto-hospedadas detrás de un firewall no recibirán web