ProductosIntegracionesRecursosDocumentaciónPrecios
Empezar ahora

© 2026 CapSolver. All rights reserved.

Contáctenos

Slack: lola@capsolver.com

Productos

  • reCAPTCHA v2
  • reCAPTCHA v3
  • Cloudflare Turnstile
  • Cloudflare Challenge
  • AWS WAF
  • Extensión de navegador
  • Más tipos de CAPTCHA

Integraciones

  • Selenium
  • Playwright
  • Puppeteer
  • n8n
  • Socios
  • Ver todas las integraciones

Recursos

  • Programa de referidos
  • Documentación
  • Referencia de API
  • Blog
  • Preguntas frecuentes
  • Glosario
  • Estado

Legal

  • Términos de servicio
  • Política de privacidad
  • Política de reembolso
  • No vender mi información personal
//Cómo resolver Cloudflare Challenge en n8n con CapSolver
Mar10, 2026

Cómo resolver Cloudflare Challenge en n8n con CapSolver

Aloísio Vítor

Aloísio Vítor

Image Processing Expert

La protección contra bots de Cloudflare va mucho más allá de los widgets CAPTCHA. El Desafío de Cloudflare — la pantalla de página completa "Verificando tu navegador…" que bloquea el acceso a un sitio completamente — es una de las defensas contra bots más agresivas en la web. Ejecuta un desafío completo de JavaScript en un entorno real de navegador, verifica señales de comportamiento y obtiene la huella digital de tu conexión TLS antes de permitirte el acceso.

Las herramientas de automatización estándar fallan aquí. No porque no puedan resolver el desafío, sino porque Cloudflare obtiene la huella digital del propio handshake TLS — y cualquier cliente HTTP no navegador es bloqueado antes de que se sirva el desafío, o vuelve a ser desafiado inmediatamente después, incluso con una cookie válida cf_clearance.

En esta guía, aprenderás cómo construir un scraper para el Desafío de Cloudflare en n8n que realmente funciona — combinando CapSolver para resolver el desafío, un servidor TLS local en Go para falsificar la huella digital TLS de Chrome, y un flujo de trabajo en n8n para unirlo todo.

Lo que construirás:

  • Un flujo de trabajo de doble disparador (programado + webhook) que pasa por alto el Bot Management de Cloudflare
  • Un servidor Go ligero que realiza solicitudes HTTP con una huella TLS real de Chrome
  • Una plantilla de scraper reutilizable que puedes dirigir a cualquier sitio protegido por CF
  • Flujos de casos de uso para scraping, inicio de sesión en cuenta, y una API independiente para resolver el desafío — todo detrás del Desafío de Cloudflare
  • Flujos de trabajo con Turnstile — una API solucionadora, una plantilla de scraping y un flujo de inicio de sesión para sitios que usan Cloudflare Turnstile (sin necesidad de proxy ni servidor TLS)

¿Qué es el Desafío de Cloudflare?

El Desafío de Cloudflare (también llamado desafío JS o desafío de Bot Management) es un intersticial de página completa que Cloudflare inserta antes de que un visitante pueda acceder a un sitio protegido. Lo has visto: una pantalla negra o blanca con "Verificando tu navegador…" o "Solo un momento…" y una barra de carga o el logo de Cloudflare.

A diferencia de Turnstile — que es un pequeño widget incrustado dentro de una página — el Desafío de Cloudflare ocupa toda la página. No puedes acceder a ningún contenido hasta que se complete.

Desafío de Cloudflare Cloudflare Turnstile
Dónde aparece Intersticial de página completa — bloquea el acceso al sitio completamente Widget incrustado dentro de una página (por ejemplo, formulario de inicio de sesión)
Cómo luce Pantalla de carga "Verificando tu navegador…" Una pequeña casilla de verificación o widget invisible en un formulario
Quién lo añade Cloudflare lo añade automáticamente basado en reglas de seguridad El propietario del sitio lo inserta en su HTML
Enfoque para resolver AntiCloudflareTask — requiere proxy AntiTurnstileTaskProxyLess — no requiere proxy
Cookie devuelta cf_clearance (específica de dominio, vinculada a IP) Token de Turnstile (de corta duración, se usa una vez)
¿Requiere proxy? Sí — la misma IP debe usarse para resolver y para la obtención No

Si ves una casilla de verificación o widget incrustado dentro de un formulario, eso es Turnstile — no este desafío. Consulta la guía de CapSolver para identificar tipos de desafío si tienes dudas.


Por qué fallan los clientes HTTP estándar

Aquí está el problema que la mayoría de las guías omiten: Cloudflare verifica tu huella digital TLS, no solo tus cookies.

Cuando un navegador se conecta a un sitio web vía HTTPS, envía un ClientHello TLS que incluye detalles sobre los conjuntos de cifrado soportados, extensiones y configuraciones. Cloudflare registra esta huella digital (llamada huella JA3 o JA4) y la compara con perfiles de navegadores conocidos.

Las bibliotecas net/http de Go, requests de Python, curl y la mayoría de librerías HTTP tienen huellas TLS distintas. Incluso si CapSolver resuelve exitosamente el desafío y devuelve una cookie válida cf_clearance, Cloudflare volverá a desafiar o bloqueará la solicitud si detecta una huella TLS no navegador en la petición subsecuente.

La solución: un servidor Go usando httpcloak — una librería que falsifica una pila TLS real de Chrome incluyendo:

  • Huella JA3 / JA4
  • Tramas HTTP/2 SETTINGS
  • Negociación ALPN
  • Orden y valores de cabeceras

Esto hace que la solicitud luzca exactamente como una solicitud del navegador Chrome a nivel de red.


Requisitos Previos

Requisito Notas
n8n autohospedado Requerido — el servidor TLS debe ejecutarse en la misma máquina que n8n. n8n Cloud no es adecuado para este caso.
Cuenta CapSolver Regístrate aquí y obtén tu clave API
Go 1.21+ Debe estar instalado en el servidor de n8n. Verifica con go version.
Proxy residencial o móvil Los proxies de centro de datos fallarán en la mayoría de los sitios protegidos por Cloudflare. Ver Requisitos de Proxy abajo.

Configuración de CapSolver en n8n

CapSolver está disponible como una integración oficial en n8n — no se requiere instalar nodos de la comunidad.

Paso 1: Abre la página de Credenciales

Ve a tu instancia n8n y navega a Configuración → Credenciales.

Página de credenciales de n8n mostrando la cuenta CapSolver

Paso 2: Crea la credencial CapSolver

  1. Haz clic en Crear credencial (arriba a la derecha)
  2. Busca "CapSolver" y selecciona CapSolver API
  3. Ingresa tu clave API del Panel de CapSolver
  4. Deja Dominios permitidos para solicitudes HTTP en All
  5. Haz clic en Guardar

Deberías ver un banner verde que indica "Conexión probada correctamente".

Configuración de credencial CapSolver con prueba de conexión exitosa

Importante: Cada nodo CapSolver en tus flujos de trabajo hará referencia a esta credencial. Solo necesitas crearla una vez — todos tus flujos solucionadores compartirán la misma credencial.


Paso 1 — Construye el servidor TLS

Este servidor Go recibe solicitudes de obtención de n8n y las ejecuta usando el perfil TLS Chrome de httpcloak. Es un binario pequeño y auto contenido que ejecutas junto a n8n.

Crea el archivo fuente

Crea un directorio y guarda lo siguiente en main.go:

bash Copy
mkdir -p ~/tls-server && cd ~/tls-server
go Copy
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/sardanioss/httpcloak/client"
)

type FetchRequest struct {
	URL     string            `json:"url"`
	Method  string            `json:"method"`
	Headers map[string]string `json:"headers"`
	Proxy   string            `json:"proxy"`
	Body    string            `json:"body"`
}

type FetchResponse struct {
	Status  int                 `json:"status"`
	Body    string              `json:"body"`
	Headers map[string][]string `json:"headers"`
}

type ErrorResponse struct {
	Error string `json:"error"`
}

func writeError(w http.ResponseWriter, status int, msg string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(ErrorResponse{Error: msg})
}

func fetchHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "only POST allowed")
		return
	}

	var req FetchRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
		return
	}

	if req.URL == "" {
		writeError(w, http.StatusBadRequest, "url is required")
		return
	}
	if req.Method == "" {
		req.Method = "GET"
	}

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	c := client.NewClient("chrome-145", client.WithTimeout(60*time.Second))
	defer c.Close()

	if req.Proxy != "" {
		c.SetProxy(req.Proxy)
	}

	// Extract user-agent separately so httpcloak uses it instead of its preset value.
	headers := make(map[string][]string, len(req.Headers))
	var userAgent string
	for k, v := range req.Headers {
		lower := strings.ToLower(k)
		if lower == "user-agent" {
			userAgent = v
		} else {
			headers[k] = []string{v}
		}
	}

	var bodyReader io.Reader
	if req.Body != "" {
		bodyReader = strings.NewReader(req.Body)
	}

	hcReq := &client.Request{
		Method:    strings.ToUpper(req.Method),
		URL:       req.URL,
		Headers:   headers,
		Body:      bodyReader,
		UserAgent: userAgent,
		FetchMode: client.FetchModeNavigate,
	}

	resp, err := c.Do(ctx, hcReq)
	if err != nil {
		writeError(w, http.StatusBadGateway, "fetch failed: "+err.Error())
		return
	}

	body, err := resp.Text()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "read body failed: "+err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(FetchResponse{
		Status:  resp.StatusCode,
		Body:    body,
		Headers: resp.Headers,
	})
}

func main() {
	const port = "7878"

	mux := http.NewServeMux()
	mux.HandleFunc("/fetch", fetchHandler)
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprint(w, `{"status":"ok"}`)
	})

	log.Printf("TLS server (httpcloak chrome-145) listening on :%s", port)
	log.Fatal(http.ListenAndServe(":"+port, mux))
}

Inicializa y compila

bash Copy
go mod init tls-server
go get github.com/sardanioss/httpcloak/client
go build -o main main.go

Ejecuta el servidor

bash Copy
./main

Verifica que esté corriendo (en una nueva terminal)

bash Copy
curl http://localhost:7878/health

Esperado: {"status":"ok"}

Nota: El servidor TLS debe ejecutarse en la misma máquina que tu instancia n8n. El flujo de trabajo de n8n lo llama en http://localhost:7878/fetch. Si usas n8n Cloud, necesitarás una configuración autohospedada — esta es una de las razones por las que se recomienda fuertemente n8n autohospedado para scraping con el Desafío de Cloudflare.


Paso 2 — Permite que n8n llame a localhost

Por defecto, n8n bloquea nodos de solicitud HTTP que llamen a direcciones localhost (protección SSRF). Necesitas deshabilitar esta restricción.

Agrega la variable de entorno N8N_BLOCK_ACCESS_TO_LOCALHOST=false y reinicia tu instancia n8n. Cómo lo hagas depende de cómo ejecutes n8n:

Si ejecutas n8n directamente:

bash Copy
export N8N_BLOCK_ACCESS_TO_LOCALHOST=false
n8n start

Si usas Docker:

Agrega -e N8N_BLOCK_ACCESS_TO_LOCALHOST=false a tu comando docker run, o añádelo en la sección environment de tu docker-compose.yml.


Flujo de trabajo: API solucionadora del Desafío de Cloudflare

Este flujo crea un endpoint POST que acepta una URL protegida por Cloudflare y un proxy, resuelve el desafío usando CapSolver y devuelve la cookie cruda cf_clearance y el userAgent. No se necesita servidor TLS — tu aplicación maneja la obtención.

Cómo funciona

Copy
Webhook (POST /solver-cloudflare-challenge) → Desafío de Cloudflare (CapSolver)
                                             → Formatear solución → Responder al webhook

4 nodos, solo webhook, sin ruta programada, sin dependencia de servidor TLS.

  1. Webhook — Recibe solicitudes POST con websiteURL y proxy
  2. Desafío de Cloudflare — CapSolver resuelve el desafío usando AntiCloudflareTask
  3. Formatear solución — Serializa el objeto cookies a una cadena de cookies, maneja errores con continueOnFail
  4. Responder al webhook — Devuelve cf_clearance, cadena de cookies serializada y userAgent### Configuración del Nodo

1. Nodo Webhook

Configuración Valor
Método HTTP POST
Ruta solver-cloudflare-challenge
Responder Response Node

Esto crea un endpoint en: https://tu-instancia-n8n.com/webhook/solver-cloudflare-challenge

2. Nodo CapSolver (Desafío Cloudflare)

Parámetro Valor Descripción
Operación Cloudflare Challenge Selecciona AntiCloudflareTask
Tipo AntiCloudflareTask Desafío completo de página de Cloudflare
URL del sitio web ={{ $json.body.websiteURL }} La URL protegida por Cloudflare
Proxy ={{ $json.body.proxy }} Proxy residencial en formato host:port:user:pass
Continuar en caso de error true Devuelve errores estructurados en lugar de fallar

CapSolver lanza un navegador real a través de tu proxy, carga la URL objetivo y resuelve el desafío de Cloudflare. Cuando tiene éxito, devuelve un objeto solution que contiene:

  • cookies — un objeto { cf_clearance: "..." } con la cookie de autorización
  • userAgent — la cadena exacta del User-Agent que el navegador usó durante la resolución

Nota: El campo es websiteURL (no targetURL) — coincide con el nombre del campo usado por todas las demás APIs de Solver y el propio nodo CapSolver.

3. Formatear la Solución (Nodo Código)

Este nodo es necesario porque AntiCloudflareTask devuelve cookies como un objeto ({ cf_clearance: "..." }), no como una cadena simple de token como reCAPTCHA o Turnstile. Serializa la cookie, extrae cf_clearance y devuelve un error estructurado si CapSolver falló.

javascript Copy
const input = $input.first().json;

if (input.error || !input.data || !input.data.solution) {
  const errorMsg = input.error
    ? (input.error.message || JSON.stringify(input.error))
    : 'No se devolvió solución — el sitio puede no estar mostrando un desafío';
  return [{ json: { success: false, error: errorMsg } }];
}

const solution = input.data.solution;
const cookies = solution.cookies;
const cfClearance = (cookies && typeof cookies === 'object')
  ? (cookies.cf_clearance || '') : '';
const cookieString = (cookies && typeof cookies === 'object')
  ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')
  : (cookies || '');

return [{ json: {
  success: true,
  cf_clearance: cfClearance,
  cookies: cookieString,
  userAgent: solution.userAgent || '',
  solvedAt: new Date().toISOString()
}}];

4. Responder al Webhook

Configuración Valor
Responder con JSON
Cuerpo de la respuesta ={{ JSON.stringify($json) }}

Prueba

bash Copy
curl -X POST https://tu-instancia-n8n.com/webhook/solver-cloudflare-challenge \
  -H "Content-Type: application/json" \
  -d '{
    "websiteURL": "https://protected-site.com",
    "proxy": "host:port:user:pass"
  }'

Respuesta exitosa:

json Copy
{
  "success": true,
  "cf_clearance": "abc123...",
  "cookies": "cf_clearance=abc123...",
  "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/145.0.0.0 ...",
  "solvedAt": "2026-03-11T12:00:00.000Z"
}

Respuesta fallida (no se encontró desafío, proxy incorrecto, etc.):

json Copy
{
  "success": false,
  "error": "No se devolvió solución — el sitio puede no estar mostrando un desafío"
}

Importa este Flujo de Trabajo

Copia el JSON a continuación e impórtalo en n8n mediante Menu → Import from JSON. Después de importarlo, selecciona tus credenciales de CapSolver en el nodo Cloudflare Challenge.

Haz clic para expandir el JSON del flujo de trabajo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Cloudflare Challenge \u2014 Solver API\n\n### Cómo funciona\n\n1. Recibe una solicitud vía webhook para resolver un desafío de Cloudflare.\n2. Usa el nodo Cloudflare Challenge para intentar resolver el desafío.\n3. Formatea el token del desafío resuelto vía código personalizado.\n4. Responde a la solicitud original con el token resuelto.\n\n### Pasos para configurar\n\n- [ ] Configurar el nodo Webhook para recibir solicitudes.\n- [ ] Configurar las credenciales de CapSolver para habilitar la resolución de desafíos de Cloudflare.\n- [ ] Asegurarse que el nodo Response esté configurado correctamente para devolver el token resuelto.\n\n### Personalización\n\nPuedes personalizar el código en el nodo 'Format Solution' para cambiar el formato de la respuesta con el token resuelto.",
        "width": 480,
        "height": 672
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -816,
        -128
      ],
      "id": "254f3829-0e6e-4ae3-bf83-85851be9a7bc",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Recibir y resolver desafío\n\nSe activa con una nueva solicitud e intenta resolver el desafío de Cloudflare.",
        "width": 512,
        "height": 304,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -256,
        -128
      ],
      "id": "44e79738-a2f0-41ec-8ff0-514afbd6cc45",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Formatear y devolver solución\n\nFormatea la solución y responde con el token resuelto.",
        "width": 496,
        "height": 304,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        352,
        -128
      ],
      "id": "fc40112d-389d-4d4e-8872-af2ae533c513",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "solver-cloudflare-challenge",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -208,
        0
      ],
      "id": "cf770001-7777-7777-7777-777777777701",
      "name": "Receive Solver Request",
      "webhookId": "cf770001-aaaa-bbbb-cccc-777777777701",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.body.websiteURL }}",
        "proxy": "={{ $json.body.proxy }}",
        "userAgent": "={{ $json.body.userAgent }}",
        "html": "={{ $json.body.html }}"
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        112,
        0
      ],
      "id": "cf770001-7777-7777-7777-777777777702",
      "name": "Cloudflare Challenge",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\n\nif (input.error || !input.data || !input.data.solution) {\n  const errorMsg = input.error\n    ? (input.error.message || JSON.stringify(input.error))\n    : 'No se devolvió solución \u2014 el sitio puede no estar mostrando un desafío';\n  return [{ json: { success: false, error: errorMsg } }];\n}\n\nconst solution = input.data.solution;\nconst cookies = solution.cookies;\nconst cfClearance = (cookies && typeof cookies === 'object') ? (cookies.cf_clearance || '') : '';\nconst cookieString = (cookies && typeof cookies === 'object')\n  ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n  : (cookies || '');\n\nreturn [{ json: {\n  success: true,\n  cf_clearance: cfClearance,\n  cookies: cookieString,\n  userAgent: solution.userAgent || '',\n  solvedAt: new Date().toISOString()\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        0
      ],
      "id": "cf770001-7777-7777-7777-777777777703",
      "name": "Format Solution"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        700,
        0
      ],
      "id": "cf770001-7777-7777-7777-777777777704",
      "name": "Return Solved Token"
    }
  ],
  "connections": {
    "Receive Solver Request": {
      "main": [
        [
          {
            "node": "Cloudflare Challenge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cloudflare Challenge": {
      "main": [
        [
          {
            "node": "Format Solution",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Solution": {
      "main": [
        [
          {
            "node": "Return Solved Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}

Flujo de Trabajo: Usando Soluciones del Desafío Cloudflare

Hasta ahora, la API de Solver anterior muestra cómo obtener una cookie cf_clearance resuelta y el userAgent. Pero, ¿qué se hace realmente con ellos?

A diferencia de reCAPTCHA o Turnstile donde envías un token en un campo de formulario, el desafío de Cloudflare devuelve una cookie (cf_clearance) que debe enviarse como un encabezado en cada solicitud subsecuente. La cookie está ligada a la IP del proxy y al User-Agent usados durante la resolución — ambos deben coincidir exactamente en la petición.

Aquí está el patrón general:

  1. Resolver el desafío de Cloudflare → Obtener la cookie cf_clearance y userAgent de CapSolver
  2. Preparar la solicitud → Construir encabezados con la cookie, el User-Agent correspondiente y los encabezados tipo Chrome sec-ch-ua
  3. Realizar la solicitud a través del servidor TLS → Enviar la petición vía http://localhost:7878/fetch para igualar la huella TLS de Chrome
  4. Verificar la respuesta → Comprobar si el sitio devolvió contenido real (status 200 con HTML) vs. una página de nuevo desafío
  5. Procesar el resultado → Extraer los datos necesarios usando el nodo HTML, Edit Fields, o un nodo Código

Concepto clave: Los clientes HTTP estándar fallan aquí incluso con una cookie cf_clearance válida — Cloudflare fingerprintea la negociación TLS misma. El servidor TLS en Go (httpcloak) hace que la solicitud parezca exactamente Chrome a nivel de red. Todo flujo de trabajo que accede a una página protegida por Cloudflare debe pasar por el servidor TLS.### Ejemplo: Raspador de Desafíos de Cloudflare

Flujo de trabajo de desafío Cloudflare en n8n con rutas de programación y webhook

Flujo del flujo de trabajo

Ruta de programación:

Copy
Cada 6 horas → Configurar objetivo [Programación] → Resolver Desafío Cloudflare
               → Preparar solicitud TLS → Obtener vía servidor TLS → Extraer resultado

Ruta de webhook:

Copy
Activador de Webhook → Configurar objetivo [Webhook] → Resolver Desafío Cloudflare
                      → Preparar solicitud TLS → Obtener vía servidor TLS → Extraer resultado → Responder al Webhook

Cómo funciona

  1. Configurar objetivo — Almacena targetURL y proxy (en formato host:port:user:pass). La ruta de programación usa valores codificados; la ruta de webhook los lee desde el cuerpo del POST.
  2. Resolver Desafío Cloudflare — Nodo CapSolver con onError: "continueRegularOutput" — continúa incluso si la página no muestra un desafío actualmente.
  3. Preparar solicitud TLS — Nodo de código que convierte el proxy host:port:user:pass en formato URL http://user:pass@host:port, serializa solution.cookies a una cadena de encabezado de cookies y construye encabezados de solicitud similares a Chrome con el userAgent exacto de la solución.
  4. Obtener vía servidor TLS — Solicitud HTTP a http://localhost:7878/fetch usando contentType: "raw" (no "json" — el modo JSON de n8n corrompe el cuerpo).
  5. Extraer resultado — Extrae status, body y fetchedAt de la respuesta del servidor TLS.
  6. Responder al Webhook — Devuelve el resultado como JSON (solo en la ruta de webhook).

¿Por qué contentType: "raw" y no "json"? El modo de contenido json de n8n espera parámetros del cuerpo como pares clave-valor. Si se pasa JSON.stringify($json) como cadena, n8n trata la cadena completa como un solo parámetro malformado y envía {"": ""} al servidor. Usar modo raw envía el cuerpo exactamente como evalúa la expresión.

Click para expandir el JSON del flujo de trabajo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Desafío Cloudflare \u2014 CapSolver + Programación + Webhook\n\n### Cómo funciona\n\n1. Activa la programación cada 6 horas para resolver desafíos de Cloudflare.\n2. Webhook se activa al recibir solicitud para resolver desafíos de Cloudflare.\n3. Configura objetivo y proxy para las solicitudes.\n4. Resuelve el desafío Cloudflare usando CapSolver.\n5. Envía resultados vía TLS y procesa la respuesta HTTP.\n\n### Pasos de configuración\n\n- Asegurarse de que las credenciales de CapSolver estén configuradas.\n- Confirmar que la URL del webhook esté configurada y accesible.\n- Verificar configuración de programación para activarse cada 6 horas.\n- Verificar que el endpoint http://localhost:7878/fetch esté disponible.\n\n### Personalización\n\nConsidere personalizar la URL objetivo o la configuración del proxy dentro de los nodos establecidos.",
        "width": 480,
        "height": 688
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1008,
        -112
      ],
      "id": "3c1c3e96-631a-4cf6-bbff-85fd8b1a01e8",
      "name": "Nota Adhesiva"
    },
    {
      "parameters": {
        "content": "## Desafío Cloudflare programado\n\nSe activa cada 6 horas para resolver desafío Cloudflare.",
        "width": 1696,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -448,
        -112
      ],
      "id": "e9187404-7818-4233-af66-091c90a40476",
      "name": "Nota Adhesiva1"
    },
    {
      "parameters": {
        "content": "## Desafío Cloudflare vía webhook\n\nResponde a solicitudes del solucionador vía webhook para resolver desafío Cloudflare.",
        "width": 2000,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -448,
        304
      ],
      "id": "b5d5ba23-8ed2-4afb-8113-3fa8f2fba51e",
      "name": "Nota Adhesiva2"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -400,
        0
      ],
      "id": "cf111111-1111-1111-1111-111111111101",
      "name": "Cada 6 Horas"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-s-001",
              "name": "targetURL",
              "value": "https://www.listaspam.com/busca.php?Telefono=671484239",
              "type": "string"
            },
            {
              "id": "cfg-s-002",
              "name": "proxy",
              "value": "YOUR_PROXY_HOST:PORT:USER:PASS",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -96,
        0
      ],
      "id": "cf111111-1111-1111-1111-111111111102",
      "name": "Configurar Objetivo [Programación]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.targetURL }}",
        "proxy": "={{ $json.proxy }}"
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        208,
        0
      ],
      "id": "cf111111-1111-1111-1111-111111111103",
      "name": "Resolver Desafío Cloudflare [Programación]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Target Config [Schedule]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n  if (!proxy) return '';\n  if (proxy.startsWith('http')) return proxy;\n  const parts = proxy.split(':');\n  if (parts.length === 4) {\n    return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n  }\n  return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n  const solution = capResult.data.solution;\n  const cookies = solution.cookies;\n  cookieStr = cookies && typeof cookies === 'object'\n    ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n    : (cookies || '');\n  if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n  'accept-language': 'en-US,en;q=0.9',\n  'sec-ch-ua': secChUa,\n  'sec-ch-ua-mobile': '?0',\n  'sec-ch-ua-platform': '\"Windows\"',\n  'sec-fetch-dest': 'document',\n  'sec-fetch-mode': 'navigate',\n  'sec-fetch-site': 'none',\n  'sec-fetch-user': '?1',\n  'upgrade-insecure-requests': '1',\n  'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n  url: config.targetURL,\n  method: 'GET',\n  proxy: toProxyURL(config.proxy),\n  headers\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        512,
        0
      ],
      "id": "cf111111-1111-1111-1111-111111111104",
      "name": "Preparar Solicitud TLS [Programación]"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        800,
        0
      ],
      "id": "cf111111-1111-1111-1111-111111111105",
      "name": "Obtener vía Servidor TLS [Programación]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "out-s-001",
              "name": "status",
              "value": "={{ $json.status }}",
              "type": "number"
            },
            {
              "id": "out-s-002",
              "name": "body",
              "value": "={{ $json.body }}",
              "type": "string"
            },
            {
              "id": "out-s-003",
              "name": "fetchedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1104,
        0
      ],
      "id": "cf111111-1111-1111-1111-111111111106",
      "name": "Extraer Resultado [Programación]"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "cloudflare-scraper",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -400,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111107",
      "name": "Recibir Solicitud del Solucionador",
      "webhookId": "cf111111-aaaa-bbbb-cccc-111111111107",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-w-001",
              "name": "targetURL",
              "value": "={{ $json.body.targetURL }}",
              "type": "string"
            },
            {
              "id": "cfg-w-002",
              "name": "proxy",
              "value": "={{ $json.body.proxy }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -96,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111108",
      "name": "Configurar Objetivo [Webhook]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.targetURL }}",
        "proxy": "={{ $json.proxy }}"
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        208,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111109",
      "name": "Resolver Desafío Cloudflare [Webhook]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Target Config [Webhook]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n  if (!proxy) return '';\n  if (proxy.startsWith('http')) return proxy;\n  const parts = proxy.split(':');\n  if (parts.length === 4) {\n    return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n  }\n  return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n  const solution = capResult.data.solution;\n  const cookies = solution.cookies;\n  cookieStr = cookies && typeof cookies === 'object'\n    ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n    : (cookies || '');\n  if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n  'accept-language': 'en-US,en;q=0.9',\n  'sec-ch-ua': secChUa,\n  'sec-ch-ua-mobile': '?0',\n  'sec-ch-ua-platform': '\"Windows\"',\n  'sec-fetch-dest': 'document',\n  'sec-fetch-mode': 'navigate',\n  'sec-fetch-site': 'none',\n  'sec-fetch-user': '?1',\n  'upgrade-insecure-requests': '1',\n  'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n  url: config.targetURL,\n  method: 'GET',\n  proxy: toProxyURL(config.proxy),\n  headers\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        512,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111110",
      "name": "Preparar Solicitud TLS [Webhook]"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        800,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111111",
      "name": "Obtener vía Servidor TLS [Webhook]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "out-w-001",
              "name": "status",
              "value": "={{ $json.status }}",
              "type": "number"
            },
            {
              "id": "out-w-002",
              "name": "body",
              "value": "={{ $json.body }}",
              "type": "string"
            },
            {
              "id": "out-w-003",
              "name": "fetchedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1104,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111112",
      "name": "Extraer Resultado [Webhook]"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1408,
        420
      ],
      "id": "cf111111-1111-1111-1111-111111111113",
      "name": "Devolver Token Resuelto"
    }
  ],
  "connections": {
    "Every 6 Hours": {
      "main": [
        [
          {
            "node": "Set Target Config [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Target Config [Schedule]": {
      "main": [
        [
          {
            "node": "Solve Cloudflare Challenge [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Cloudflare Challenge [Schedule]": {
      "main": [
        [
          {
            "node": "Prepare TLS Request [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare TLS Request [Schedule]": {
      "main": [
        [
          {
            "node": "Fetch via TLS Server [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch via TLS Server [Schedule]": {
      "main": [
        [
          {
            "node": "Extract Result [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Solver Request": {
      "main": [
        [
          {
            "node": "Set Target Config [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Target Config [Webhook]": {
      "main": [
        [
          {
            "node": "Solve Cloudflare Challenge [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Cloudflare Challenge [Webhook]": {
      "main": [
        [
          {
            "node": "Prepare TLS Request [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare TLS Request [Webhook]": {
      "main": [
        [
          {
            "node": "Fetch via TLS Server [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch via TLS Server [Webhook]": {
      "main": [
        [
          {
            "node": "Extract Result [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Result [Webhook]": {
      "main": [
        [
          {
            "node": "Return Solved Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}

---## Flujo de trabajo: Ejemplos de casos de uso

El ejemplo de API de Solver y scraper mostrado arriba muestra el patrón central: resolver el desafío, usar la solución para realizar la obtención a través de TLS. Los siguientes flujos de trabajo extienden este patrón a casos de uso listos para producción — cada uno con disparadores dobles (programación + webhook), seguimiento persistente del estado y salida estructurada. Todos requieren los mismos prerrequisitos: una instancia self-hosted de n8n, el servidor TLS corriendo en el puerto 7878, un proxy residencial y una credencial de CapSolver.

Flujo de trabajo Propósito
Cloudflare Challenge Scraping — Price & Product Details — CapSolver + Schedule + Webhook Extrae el precio y el nombre del producto de una página protegida por CF cada 6 horas, compara con valores anteriores y alerta sobre cambios
Cloudflare Challenge Account Login — CapSolver + Schedule + Webhook Inicia sesión en tu propia cuenta en un sitio protegido por CF resolviendo primero el desafío y luego enviando las credenciales mediante POST a través del servidor TLS
Turnstile — Solver API Expone un webhook que resuelve Turnstile y devuelve el token — no se necesita proxy ni servidor TLS
Turnstile Scraping — Price & Product Monitor Resuelve Turnstile, obtiene una página de producto con el token, extrae precio y nombre, y alerta sobre cambios
Turnstile Account Login Inicia sesión en tu propia cuenta en un sitio protegido por Turnstile resolviendo primero el desafío y luego enviando las credenciales con el token — no se necesita proxy ni servidor TLS

Este flujo de trabajo raspa una página de producto cada 6 horas (programación) o bajo demanda (webhook), extrae el precio usando el nodo HTML, y lo compara contra el valor almacenado previamente.

Ruta de programación:

Copy
Cada 6 horas → Configurar Objetivo → Resolver el desafío de CF → Preparar solicitud TLS
             → Obtener vía servidor TLS → Extraer datos → Comparar datos
             → ¿Datos cambiados? → Crear alerta / Sin cambios

Manejo de errores: Si CapSolver falla, el flujo continúa sin cookies (mediante continueOnFail). La obtención desde el servidor TLS puede aún tener éxito si la página no está mostrando un desafío actualmente.

Comportamientos clave:

  • Usa dataPropertyName: "body" (no "data") porque el servidor TLS devuelve { status, body, headers }
  • El nodo HTML extrae el precio y el nombre del producto mediante selectores CSS (.product-price, h1)
  • $workflow.staticData.lastPrice mantiene el precio previo entre ejecuciones
  • La comparación de precios detecta tanto caídas (severidad: deal) como aumentos (severidad: info)
  • Conversión automática de formato de proxy: host:port:user:pass → http://user:pass@host:port a través del ayudante toProxyURL()
Haga clic para expandir el JSON completo del flujo de trabajo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Cloudflare Challenge Scraping \u2014 Price & Product Details \u2014 CapSolver + Schedule + Webhook\n\n### Cómo funciona\n\n1. Los disparadores están configurados para verificar periódicamente el sitio web objetivo o en respuesta a un webhook externo.\n2. Se aplican configuraciones para la URL objetivo y proxies.\n3. Se resuelven los desafíos de Cloudflare para navegar las protecciones del sitio web.\n4. Se realizan solicitudes seguras para obtener datos del servidor objetivo.\n5. Los datos extraídos se comparan para detectar cambios.\n6. Se construyen y envían alertas o se retornan datos basado en los cambios detectados.\n\n### Pasos de configuración\n\n- [ ] Configure la URL y los ajustes de proxy en los nodos 'Set Target Config'.\n- [ ] Conecte las credenciales de CapSolver para resolver desafíos de Cloudflare.\n- [ ] Asegúrese de que la URL del webhook está configurada correctamente para recibir solicitudes externas.\n\n### Personalización\n\nPara ajustar la frecuencia de monitoreo, modifique el intervalo en el nodo 'Every 6 Hours' o la configuración del disparador webhook.",
        "width": 480,
        "height": 896
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1104,
        -192
      ],
      "id": "85c55c3d-335a-47e5-8721-82fa4d633033",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Configuración del disparador programado\n\nInicializa la programación cada 6 horas para comenzar el proceso de raspado de datos mediante agendamiento.",
        "width": 1392,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -544,
        -112
      ],
      "id": "6ac3bd35-66ef-43ed-a8b5-5d5f6d367fba",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Procesamiento de datos programado\n\nManeja la extracción de datos, comparación y creación de alertas tras ser activado por la programación.",
        "width": 1088,
        "height": 480,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        960,
        -192
      ],
      "id": "a1c60513-ed8b-43f2-bff3-a7b19436337f",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Configuración del disparador webhook\n\nMonitorea solicitudes externas vía webhook para iniciar el proceso de raspado.",
        "width": 1392,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -544,
        384
      ],
      "id": "ee4d9a0d-3233-4b70-828b-078c9eee0086",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Procesamiento y respuesta de datos por webhook\n\nExtrae y compara datos desde el disparador webhook; retorna respuestas basadas en cambios.",
        "width": 1392,
        "height": 480,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        960,
        336
      ],
      "id": "4ad55fbf-2882-4c63-9e09-37e674b00145",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -500,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333301",
      "name": "Every 6 Hours"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-s-001",
              "name": "targetURL",
              "value": "https://YOUR_CF_PROTECTED_SITE.com/product-page",
              "type": "string"
            },
            {
              "id": "cfg-s-002",
              "name": "proxy",
              "value": "YOUR_PROXY_HOST:PORT:USER:PASS",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -200,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333302",
      "name": "Set Target Config [Schedule]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.targetURL }}",
        "proxy": "={{ $json.proxy }}"
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        100,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333303",
      "name": "Solve Cloudflare Challenge [Schedule]",
      "onError": "continueRegularOutput",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Target Config [Schedule]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n  if (!proxy) return '';\n  if (proxy.startsWith('http')) return proxy;\n  const parts = proxy.split(':');\n  if (parts.length === 4) {\n    return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n  }\n  return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n  const solution = capResult.data.solution;\n  const cookies = solution.cookies;\n  cookieStr = cookies && typeof cookies === 'object'\n    ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n    : (cookies || '');\n  if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n  'accept-language': 'en-US,en;q=0.9',\n  'sec-ch-ua': secChUa,\n  'sec-ch-ua-mobile': '?0',\n  'sec-ch-ua-platform': '\"Windows\"',\n  'sec-fetch-dest': 'document',\n  'sec-fetch-mode': 'navigate',\n  'sec-fetch-site': 'none',\n  'sec-fetch-user': '?1',\n  'upgrade-insecure-requests': '1',\n  'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n  url: config.targetURL,\n  method: 'GET',\n  proxy: toProxyURL(config.proxy),\n  headers\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333304",
      "name": "Preparar solicitud TLS [Schedule]"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        700,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333305",
      "name": "Obtener vía servidor TLS [Schedule]"
    },
    {
      "parameters": {
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "price",
              "cssSelector": ".product-price, [data-price], .price",
              "returnValue": "text",
              "returnArray": false
            },
            {
              "key": "productName",
              "cssSelector": "h1, .product-title",
              "returnValue": "text",
              "returnArray": false
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        1000,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333306",
      "name": "Extraer Datos"
    },
    {
      "parameters": {
        "jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Producto';\n\nconst parsePrice = (str) => {\n  if (!str) return null;\n  const match = str.match(/[\\d]+\\.?\\d*/);\n  return match ? parseFloat(match[0].replace(',', '')) : null;\n};\n\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\n\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\n\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'bajó' : 'subió') : 'sin cambios';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\n\nreturn [{\n  json: {\n    productName,\n    currentPrice,\n    previousPrice: previousPrice || 'primera revisión',\n    changed,\n    direction,\n    diff: changed ? `$${diff}` : null,\n    checkedAt: new Date().toISOString()\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333307",
      "name": "Comparar Datos"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "price-if-001",
              "leftValue": "={{ $json.changed }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1600,
        0
      ],
      "id": "cf333333-3333-3333-3333-333333333308",
      "name": "¿Datos Cambiados?"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "alert-001",
              "name": "alert",
              "value": "=Precio {{ $json.direction }} para {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }} ({{ $json.direction === 'bajó' ? '-' : '+' }}{{ $json.diff }})",
              "type": "string"
            },
            {
              "id": "alert-002",
              "name": "severity",
              "value": "={{ $json.direction === 'bajó' ? 'deal' : 'info' }}",
              "type": "string"
            },
            {
              "id": "alert-003",
              "name": "checkedAt",
              "value": "={{ $json.checkedAt }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1900,
        -80
      ],
      "id": "cf333333-3333-3333-3333-333333333309",
      "name": "Crear Alerta"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "nc-001",
              "name": "status",
              "value": "sin_cambios",
              "type": "string"
            },
            {
              "id": "nc-002",
              "name": "currentPrice",
              "value": "={{ $json.currentPrice }}",
              "type": "string"
            },
            {
              "id": "nc-003",
              "name": "checkedAt",
              "value": "={{ $json.checkedAt }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1900,
        128
      ],
      "id": "cf333333-3333-3333-3333-333333333310",
      "name": "Sin Cambios"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "cloudflare-price-monitor",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -500,
        500
      ],
      "id": "cf333333-3333-3333-3333-333333333311",
      "name": "Recibir Solicitud de Monitoreo",
      "webhookId": "cf333333-aaaa-bbbb-cccc-333333333311",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-w-001",
              "name": "targetURL",
              "value": "={{ $json.body.targetURL }}",
              "type": "string"
            },
            {
              "id": "cfg-w-002",
              "name": "proxy",
              "value": "={{ $json.body.proxy }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -200,
        500
      ],
      "id": "cf333333-3333-3333-3333-333333333312",
      "name": "Set Target Config [Webhook]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.targetURL }}",
        "proxy": "={{ $json.proxy }}"
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        100,
        500
      ],
      "id": "cf333333-3333-3333-3333-333333333313",
      "name": "Resolver desafío Cloudflare [Webhook]",
      "onError": "continueRegularOutput",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Target Config [Webhook]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n  if (!proxy) return '';\n  if (proxy.startsWith('http')) return proxy;\n  const parts = proxy.split(':');\n  if (parts.length === 4) {\n    return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n  }\n  return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n  const solution = capResult.data.solution;\n  const cookies = solution.cookies;\n  cookieStr = cookies && typeof cookies === 'object'\n    ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n    : (cookies || '');\n  if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n  'accept-language': 'en-US,en;q=0.9',\n  'sec-ch-ua': secChUa,\n  'sec-ch-ua-mobile': '?0',\n  'sec-ch-ua-platform': '\"Windows\"',\n  'sec-fetch-dest': 'document',\n  'sec-fetch-mode': 'navigate',\n  'sec-fetch-site': 'none',\n  'sec-fetch-user': '?1',\n  'upgrade-insecure-requests': '1',\n  'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n  url: config.targetURL,\n  method: 'GET',\n  proxy: toProxyURL(config.proxy),\n  headers\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        400,
        500
      ],
      "id": "cf333333-3333-3333-3333-333333333314",
      "name": "Preparar solicitud TLS [Webhook]"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        700,
        500
      ],
      "id": "cf333333-3333-3333-3333-333333333315",
      "name": "Obtener vía servidor TLS [Webhook]"
    },
    {
      "parameters": {
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "price",
              "cssSelector": ".product-price, [data-price], .price",
              "returnValue": "text",
              "returnArray": false
            },
            {
              "key": "productName",
              "cssSelector": "h1, .product-title",
              "returnValue": "text",
              "returnArray": false
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        1008,
        528
      ],
      "id": "cf333333-3333-3333-3333-333333333316",
      "name": "Extraer Datos [Webhook]"
    },
    {
      "parameters": {
        "jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Producto';\n\nconst parsePrice = (str) => {\n  if (!str) return null;\n  const match = str.match(/[\\d]+\\.?\\d*/);\n  return match ? parseFloat(match[0].replace(',', '')) : null;\n};\n\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\n\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\n\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'bajó' : 'subió') : 'sin cambios';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\n\nreturn [{\n  json: {\n    productName,\n    currentPrice,\n    previousPrice: previousPrice || 'primera revisión',\n    changed,\n    direction,\n    diff: changed ? `$${diff}` : null,\n    checkedAt: new Date().toISOString()\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1296,
        528
      ],
      "id": "cf333333-3333-3333-3333-333333333317",
      "name": "Comparar Datos [Webhook]"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "price-if-002",
              "leftValue": "={{ $json.changed }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1600,
        528
      ],
      "id": "cf333333-3333-3333-3333-333333333318",
      "name": "¿Datos Cambiados? [Webhook]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "alert-004",
              "name": "alert",
              "value": "=Precio {{ $json.direction }} para {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }} ({{ $json.direction === 'bajó' ? '-' : '+' }}{{ $json.diff }})",
              "type": "string"
            },
            {
              "id": "alert-005",
              "name": "severity",
              "value": "={{ $json.direction === 'bajó' ? 'deal' : 'info' }}",
              "type": "string"
            },
            {
              "id": "alert-006",
              "name": "checkedAt",
              "value": "={{ $json.checkedAt }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1904,
        448
      ],
      "id": "cf333333-3333-3333-3333-333333333319",
      "name": "Crear Alerta [Webhook]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "nc-004",
              "name": "status",
              "value": "sin_cambios",
              "type": "string"
            },
            {
              "id": "nc-005",
              "name": "currentPrice",
              "value": "={{ $json.currentPrice }}",
              "type": "string"
            },
            {
              "id": "nc-006",
              "name": "checkedAt",
              "value": "={{ $json.checkedAt }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1904,
        656
      ],
      "id": "cf333333-3333-3333-3333-333333333320",
      "name": "Sin Cambios [Webhook]"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        2208,
        528
      ],
      "id": "cf333333-3333-3333-3333-333333333321",
      "name": "Devolver Datos Raspados"
    }
  ],
  "connections": {
    "Every 6 Hours": {
      "main": [
        [
          {
            "node": "Set Target Config [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Target Config [Schedule]": {
      "main": [
        [
          {
            "node": "Solve Cloudflare Challenge [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Cloudflare Challenge [Schedule]": {
      "main": [
        [
          {
            "node": "Prepare TLS Request [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare TLS Request [Schedule]": {
      "main": [
        [
          {
            "node": "Fetch via TLS Server [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch via TLS Server [Schedule]": {
      "main": [
        [
          {
            "node": "Extract Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Data": {
      "main": [
        [
          {
            "node": "Compare Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Data": {
      "main": [
        [
          {
            "node": "Data Changed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Changed?": {
      "main": [
        [
          {
            "node": "Build Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Change",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Monitor Request": {
      "main": [
        [
          {
            "node": "Set Target Config [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Target Config [Webhook]": {
      "main": [
        [
          {
            "node": "Solve Cloudflare Challenge [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Cloudflare Challenge [Webhook]": {
      "main": [
        [
          {
            "node": "Prepare TLS Request [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare TLS Request [Webhook]": {
      "main": [
        [
          {
            "node": "Fetch via TLS Server [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch via TLS Server [Webhook]": {
      "main": [
        [
          {
            "node": "Extract Data [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Data [Webhook]": {
      "main": [
        [
          {
            "node": "Compare Data [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Data [Webhook]": {
      "main": [
        [
          {
            "node": "Data Changed? [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Changed? [Webhook]": {
      "main": [
        [
          {
            "node": "Build Alert [Webhook]",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Change [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Alert [Webhook]": {
      "main": [
        [
          {
            "node": "Return Scraped Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No Change [Webhook]": {
      "main": [
        [
          {
            "node": "Return Scraped Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}
### Ejemplo 2: Inicio de sesión en la cuenta

Este flujo de trabajo automatiza el inicio de sesión en un sitio protegido por Cloudflare. Un nodo Set Login Config centraliza todos los parámetros: [Schedule] para la ruta programada y [Webhook] para la ruta bajo demanda por webhook.

Ruta programada:

Copy
Cada 24 horas → Set Login Config → Resolver desafío CF
             → Preparar solicitud de inicio de sesión TLS → Enviar inicio de sesión vía servidor TLS
             → ¿Inicio de sesión exitoso? → Marcar éxito / Marcar fallo

Manejo de errores: Si CapSolver falla, el flujo de trabajo continúa sin cookies (mediante continueOnFail). La solicitud de inicio de sesión probablemente fallará, lo cual detecta el nodo ¿Inicio de sesión exitoso?.

Comportamientos clave:

  • Utiliza la cookie cf_clearance + userAgent como encabezados HTTP (sin token en el cuerpo del formulario — a diferencia del inicio de sesión reCAPTCHA que envía g-recaptcha-response)
  • Campos del formulario mediante URLSearchParams — edite los nombres de campos (usernameField, passwordField) en Set Login Config para que coincidan con su sitio
  • ¿Inicio de sesión exitoso? verifica status < 400 Y successMarker en el cuerpo de la respuesta
  • La ruta webhook devuelve el resultado como JSON mediante Responder a Webhook
Haga clic para expandir el JSON completo del flujo de trabajo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Inicio de sesión en cuenta con desafío Cloudflare \u2014 CapSolver + Schedule + Webhook\n\n### Cómo funciona\n\n1. Programar un proceso de inicio de sesión cada 24 horas y resolver el desafío de Cloudflare.\n2. Preparar y enviar una solicitud de inicio de sesión TLS cuando sea iniciado por el programador.\n3. Manejar el éxito o fracaso del inicio de sesión programado, incluyendo registrar resultados.\n4. Recibir solicitudes de inicio de sesión vía webhook y resolver el desafío Cloudflare.\n5. Preparar y enviar una solicitud TLS de inicio de sesión cuando sea iniciada por el webhook.\n6. Manejar éxito o fracaso del inicio de sesión vía webhook y devolver resultados.\n\n### Pasos de configuración\n\n- [ ] Asegúrese de que las credenciales API de CapSolver para Cloudflare estén configuradas.\n- [ ] Configurar un servidor TLS corriendo localmente en http://localhost:7878.\n- [ ] Configurar la URL del webhook para recibir solicitudes de inicio de sesión.\n\n### Personalización\n\nAjuste los intervalos programados para cumplir con necesidades específicas de frecuencia.",
        "width": 480,
        "height": 896
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1248,
        -320
      ],
      "id": "ba1d6098-8cd2-40f1-b9ae-b945303e5d12",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Inicio del login programado\n\nInicia el proceso de inicio de sesión cada 24 horas como punto de disparo para acciones posteriores.",
        "width": 240,
        "height": 336,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -688,
        -256
      ],
      "id": "acc8f2a2-298f-4d1b-9db7-8a98fe626abb",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Flujo de trabajo de login programado\n\nManeja el desafío Cloudflare, prepara y envía la solicitud de login, y verifica si fue exitosa cada 24 horas.",
        "width": 1328,
        "height": 480,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -416,
        -320
      ],
      "id": "1259d9f4-0b54-4693-89af-193f3ccda6a0",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Inicio del login vía webhook\n\nAcepta solicitudes de inicio de sesión mediante webhook para iniciar el flujo de trabajo subsecuente.",
        "width": 240,
        "height": 320,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -688,
        272
      ],
      "id": "e95374d2-4029-469d-8e8f-a9afb66ae2ed",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Flujo de trabajo de login vía webhook\n\nProcesa las solicitudes de login recibidas por webhook, maneja el desafío Cloudflare, prepara y envía la solicitud de inicio de sesión, verifica el éxito y registra los resultados.",
        "width": 1216,
        "height": 528,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -416,
        192
      ],
      "id": "da56a87e-6675-439a-8c65-7da631a86df1",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "content": "## Devolver resultado del webhook\n\nResponde a la solicitud inicial de webhook con el resultado del intento de inicio de sesión.",
        "width": 240,
        "height": 320,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        832,
        256
      ],
      "id": "7a3f246c-ebd5-419f-a955-b01669743b31",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -640,
        -80
      ],
      "id": "cf666666-6666-6666-6666-666666666601",
      "name": "Cada 24 horas"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "login-001",
              "name": "targetURL",
              "value": "https://YOUR_CF_PROTECTED_SITE.com/login",
              "type": "string"
            },
            {
              "id": "login-002",
              "name": "loginActionURL",
              "value": "https://YOUR_CF_PROTECTED_SITE.com/login",
              "type": "string"
            },
            {
              "id": "login-003",
              "name": "proxy",
              "value": "YOUR_PROXY_HOST:PORT:USER:PASS",
              "type": "string"
            },
            {
              "id": "login-004",
              "name": "usernameField",
              "value": "email",
              "type": "string"
            },
            {
              "id": "login-005",
              "name": "passwordField",
              "value": "password",
              "type": "string"
            },
            {
              "id": "login-006",
              "name": "usernameValue",
              "value": "your-email@example.com",
              "type": "string"
            },
            {
              "id": "login-007",
              "name": "passwordValue",
              "value": "YOUR_ACCOUNT_PASSWORD",
              "type": "string"
            },
            {
              "id": "login-008",
              "name": "successMarker",
              "value": "account-dashboard",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -368,
        -80
      ],
      "id": "cf666666-6666-6666-6666-666666666602",
      "name": "Set Login Config [Schedule]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.targetURL }}",
        "proxy": "={{ $json.proxy }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        -144,
        -64
      ],
      "id": "cf666666-6666-6666-6666-666666666603",
      "name": "Resolver desafío Cloudflare [Schedule]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Login Config [Schedule]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n  if (!proxy) return '';\n  if (proxy.startsWith('http')) return proxy;\n  const parts = proxy.split(':');\n  if (parts.length === 4) {\n    return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n  }\n  return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n  const solution = capResult.data.solution;\n  const cookies = solution.cookies;\n  cookieStr = cookies && typeof cookies === 'object'\n    ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n    : (cookies || '');\n  if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst params = new URLSearchParams();\nparams.set(config.usernameField, config.usernameValue);\nparams.set(config.passwordField, config.passwordValue);\n\nconst headers = {\n  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n  'accept-language': 'en-US,en;q=0.9',\n  'content-type': 'application/x-www-form-urlencoded',\n  'sec-ch-ua': secChUa,\n  'sec-ch-ua-mobile': '?0',\n  'sec-ch-ua-platform': '\"Windows\"',\n  'sec-fetch-dest': 'document',\n  'sec-fetch-mode': 'navigate',\n  'sec-fetch-site': 'same-origin',\n  'sec-fetch-user': '?1',\n  'upgrade-insecure-requests': '1',\n  'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n  url: config.loginActionURL,\n  method: 'POST',\n  proxy: toProxyURL(config.proxy),\n  headers,\n  body: params.toString()\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48,
        -64
      ],
      "id": "cf666666-6666-6666-6666-666666666604",
      "name": "Preparar solicitud TLS de inicio de sesión [Schedule]"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        240,
        -64
      ],
      "id": "cf666666-6666-6666-6666-666666666605",
      "name": "Enviar inicio de sesión vía servidor TLS [Schedule]"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "login-if-001",
              "leftValue": "={{ $json.status < 400 && String($json.body || '').includes($('Set Login Config [Schedule]').item.json.successMarker) }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        432,
        -64
      ],
      "id": "cf666666-6666-6666-6666-666666666606",
      "name": "¿Inicio de sesión exitoso? [Schedule]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "login-010",
              "name": "action",
              "value": "account_login",
              "type": "string"
            },
            {
              "id": "login-011",
              "name": "status",
              "value": "success",
              "type": "string"
            },
            {
              "id": "login-012",
              "name": "message",
              "value": "Flujo de inicio de sesión configurado exitosamente (mediante evasión del desafío Cloudflare)",
              "type": "string"
            },
            {
              "id": "login-013",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        768,
        -208
      ],
      "id": "cf666666-6666-6666-6666-666666666607",
      "name": "Marcar éxito de inicio de sesión [Schedule]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "login-014",
              "name": "action",
              "value": "account_login",
              "type": "string"
            },
            {
              "id": "login-015",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "login-016",
              "name": "statusCode",
              "value": "={{ $json.status }}",
              "type": "number"
            },
            {
              "id": "login-017",
              "name": "message",
              "value": "La respuesta de inicio de sesión no coincidió con el marcador de éxito configurado",
              "type": "string"
            },
            {
              "id": "login-018",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        768,
        0
      ],
      "id": "cf666666-6666-6666-6666-666666666608",
      "name": "Marcar fallo de inicio de sesión [Schedule]"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "cloudflare-account-login",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -640,
        432
      ],
      "id": "cf666666-6666-6666-6666-666666666609",
      "name": "Recibir solicitud de login",
      "webhookId": "cf666666-aaaa-bbbb-cccc-666666666609"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "login-019",
              "name": "targetURL",
              "value": "={{ $json.body.targetURL }}",
              "type": "string"
            },
            {
              "id": "login-020",
              "name": "loginActionURL",
              "value": "={{ $json.body.loginActionURL }}",
              "type": "string"
            },
            {
              "id": "login-021",
              "name": "proxy",
              "value": "={{ $json.body.proxy }}",
              "type": "string"
            },
            {
              "id": "login-022",
              "name": "usernameField",
              "value": "={{ $json.body.usernameField }}",
              "type": "string"
            },
            {
              "id": "login-023",
              "name": "passwordField",
              "value": "={{ $json.body.passwordField }}",
              "type": "string"
            },
            {
              "id": "login-024",
              "name": "usernameValue",
              "value": "={{ $json.body.usernameValue }}",
              "type": "string"
            },
            {
              "id": "login-025",
              "name": "passwordValue",
              "value": "={{ $json.body.passwordValue }}",
              "type": "string"
            },
            {
              "id": "login-026",
              "name": "successMarker",
              "value": "={{ $json.body.successMarker }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -368,
        480
      ],
      "id": "cf666666-6666-6666-6666-666666666610",
      "name": "Set Login Config [Webhook]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Challenge",
        "websiteURL": "={{ $json.targetURL }}",
        "proxy": "={{ $json.proxy }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        -144,
        480
      ],
      "id": "cf666666-6666-6666-6666-666666666611",
      "name": "Resolver desafío Cloudflare [Webhook]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Login Config [Webhook]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n  if (!proxy) return '';\n  if (proxy.startsWith('http')) return proxy;\n  const parts = proxy.split(':');\n  if (parts.length === 4) {\n    return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n  }\n  return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n  const solution = capResult.data.solution;\n  const cookies = solution.cookies;\n  cookieStr = cookies && typeof cookies === 'object'\n    ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n    : (cookies || '');\n  if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst params = new URLSearchParams();\nparams.set(config.usernameField, config.usernameValue);\nparams.set(config.passwordField, config.passwordValue);\n\nconst headers = {\n  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n  'accept-language': 'en-US,en;q=0.9',\n  'content-type': 'application/x-www-form-urlencoded',\n  'sec-ch-ua': secChUa,\n  'sec-ch-ua-mobile': '?0',\n  'sec-ch-ua-platform': '\"Windows\"',\n  'sec-fetch-dest': 'document',\n  'sec-fetch-mode': 'navigate',\n  'sec-fetch-site': 'same-origin',\n  'sec-fetch-user': '?1',\n  'upgrade-insecure-requests': '1',\n  'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n  url: config.loginActionURL,\n  method: 'POST',\n  proxy: toProxyURL(config.proxy),\n  headers,\n  body: params.toString()\n}}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48,
        480
      ],
      "id": "cf666666-6666-6666-6666-666666666612",
      "name": "Preparar solicitud TLS de inicio de sesión [Webhook]"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        256,
        480
      ],
      "id": "cf666666-6666-6666-6666-666666666613",
      "name": "Enviar inicio de sesión vía servidor TLS [Webhook]"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "login-if-002",
              "leftValue": "={{ $json.status < 400 && String($json.body || '').includes($('Set Login Config [Webhook]').item.json.successMarker) }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        448,
        496
      ],
      "id": "cf666666-6666-6666-6666-666666666614",
      "name": "¿Inicio de sesión exitoso? [Webhook]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "login-028",
              "name": "action",
              "value": "account_login",
              "type": "string"
            },
            {
              "id": "login-029",
              "name": "status",
              "value": "success",
              "type": "string"
            },
            {
              "id": "login-030",
              "name": "message",
              "value": "Flujo de inicio de sesión configurado exitosamente (mediante evasión del desafío Cloudflare)",
              "type": "string"
            },
            {
              "id": "login-031",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        640,
        320
      ],
      "id": "cf666666-6666-6666-6666-666666666615",
      "name": "Marcar éxito de inicio de sesión [Webhook]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "login-032",
              "name": "action",
              "value": "account_login",
              "type": "string"
            },
            {
              "id": "login-033",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "login-034",
              "name": "statusCode",
              "value": "={{ $json.status }}",
              "type": "number"
            },
            {
              "id": "login-035",
              "name": "message",
              "value": "La respuesta de inicio de sesión no coincidió con el marcador de éxito configurado",
              "type": "string"
            },
            {
              "id": "login-036",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        656,
        544
      ],
      "id": "cf666666-6666-6666-6666-666666666616",
      "name": "Marcar fallo de inicio de sesión [Webhook]"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        880,
        416
      ],
      "id": "cf666666-6666-6666-6666-666666666617",
      "name": "Devolver resultado del login"
    }
  ],
  "connections": {
    "Cada 24 horas": {
      "main": [
        [
          {
            "node": "Set Login Config [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Login Config [Schedule]": {
      "main": [
        [
          {
            "node": "Resolver desafío Cloudflare [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolver desafío Cloudflare [Schedule]": {
      "main": [
        [
          {
            "node": "Preparar solicitud TLS de inicio de sesión [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar solicitud TLS de inicio de sesión [Schedule]": {
      "main": [
        [
          {
            "node": "Enviar inicio de sesión vía servidor TLS [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar inicio de sesión vía servidor TLS [Schedule]": {
      "main": [
        [
          {
            "node": "¿Inicio de sesión exitoso? [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "¿Inicio de sesión exitoso? [Schedule]": {
      "main": [
        [
          {
            "node": "Marcar éxito de inicio de sesión [Schedule]",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Marcar fallo de inicio de sesión [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recibir solicitud de login": {
      "main": [
        [
          {
            "node": "Set Login Config [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Login Config [Webhook]": {
      "main": [
        [
          {
            "node": "Resolver desafío Cloudflare [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolver desafío Cloudflare [Webhook]": {
      "main": [
        [
          {
            "node": "Preparar solicitud TLS de inicio de sesión [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar solicitud TLS de inicio de sesión [Webhook]": {
      "main": [
        [
          {
            "node": "Enviar inicio de sesión vía servidor TLS [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar inicio de sesión vía servidor TLS [Webhook]": {
      "main": [
        [
          {
            "node": "¿Inicio de sesión exitoso? [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "¿Inicio de sesión exitoso? [Webhook]": {
      "main": [
        [
          {
            "node": "Marcar éxito de inicio de sesión [Webhook]",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Marcar fallo de inicio de sesión [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcar éxito de inicio de sesión [Webhook]": {
      "main": [
        [
          {
            "node": "Devolver resultado del login",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcar fallo de inicio de sesión [Webhook]": {
      "main": [
        [
          {
            "node": "Devolver resultado del login",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}
### Ejemplo 3: Turnstile — API del solucionador

Este flujo crea un endpoint POST que resuelve un desafío Cloudflare Turnstile y devuelve el token. Es el equivalente a Turnstile de la API de solución de desafíos de Cloudflare anterior, pero más simple: sin proxy, sin servidor TLS, solo 3 nodos.

Flujo:

Copy
Webhook (POST /solver-turnstile) → Resolver Turnstile (CapSolver) → Responder al Webhook
  1. Webhook — Recibe solicitudes POST con websiteURL y websiteKey
  2. Resolver Turnstile — CapSolver resuelve el desafío Turnstile usando AntiTurnstileTaskProxyLess
  3. Responder al Webhook — Devuelve los datos de la solución (incluyendo el token)

Diferencia clave respecto al desafío de Cloudflare: Turnstile devuelve una cadena token, no una cookie cf_clearance. En solicitudes posteriores, envías este token como encabezado cf-turnstile-response (o campo de formulario, dependiendo del sitio). No se requiere proxy.

Pruébalo

bash Copy
curl -X POST https://your-n8n-instance.com/webhook/solver-turnstile \
  -H "Content-Type: application/json" \
  -d '{
    "websiteURL": "https://target-site.com/page",
    "websiteKey": "0x4AAAAAAA..."
  }'

Importar este flujo

Copia el JSON a continuación e impórtalo en n8n vía Menú → Importar desde JSON. Después de importarlo, selecciona tu credencial de CapSolver en el nodo Resolver Turnstile.

Haga clic para expandir el JSON del flujo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Turnstile \u2014 API del solucionador\n\n### Cómo funciona\n\n1. Recibe una solicitud de solución a través de un webhook.\n2. Resuelve el CAPTCHA Turnstile usando un nodo solucionador especializado.\n3. Envía la respuesta de vuelta mediante la respuesta al webhook.\n\n### Pasos de configuración\n\n- [ ] Configurar la URL del webhook para recibir solicitudes.\n- [ ] Configurar las credenciales para el nodo capSolver.\n- [ ] Asegurarse de que la URL del webhook de respuesta esté correctamente configurada.\n\n### Personalización\n\nLa configuración del nodo solucionador puede ajustarse para manejar diferentes tipos de CAPTCHAs Turnstile.",
        "width": 480,
        "height": 560
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -848,
        -80
      ],
      "id": "d52f67cb-cb00-430f-bd76-b74cf4fe6184",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Manejar solicitud del solucionador\n\nRecibe y procesa una solicitud para resolver un CAPTCHA Turnstile, luego envía el resultado de vuelta.",
        "width": 832,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -288,
        -80
      ],
      "id": "21f62617-eaa2-41ae-8fb8-c8502f21c275",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "solver-turnstile",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -240,
        32
      ],
      "id": "ts-001",
      "name": "Recibir solicitud de solucionador",
      "webhookId": "a7ef0297-8455-44bd-9305-26c179f040b5",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "Cloudflare Turnstile",
        "websiteURL": "={{ $json.body.websiteURL }}",
        "websiteKey": "={{ $json.body.websiteKey }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        80,
        32
      ],
      "id": "ts-002",
      "name": "Resolver Turnstile",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json.data) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        400,
        32
      ],
      "id": "ts-003",
      "name": "Responder al Webhook"
    }
  ],
  "connections": {
    "Recibir solicitud de solucionador": {
      "main": [
        [
          {
            "node": "Resolver Turnstile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolver Turnstile": {
      "main": [
        [
          {
            "node": "Responder al Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}
### Ejemplo 4: Raspado Turnstile — Monitor de Precio y Producto

Este flujo de trabajo resuelve Cloudflare Turnstile, obtiene una página de producto con el token resuelto, extrae el precio y el nombre del producto, y compara con el valor anterior — alertando sobre cambios. Sigue el mismo patrón de activación dual (programación + webhook) que el ejemplo de raspado de Cloudflare Challenge anterior.

Diferencia clave con el raspado de Cloudflare Challenge: No hay servidor TLS, no hay proxy, no hay nodo de código Prepare TLS Request. El token Turnstile se envía como cabecera cf-turnstile-response directamente a través del nodo HTTP Request incorporado de n8n.

Ruta de programación:

Copy
Cada 6 horas → Configurar destino → Resolver Turnstile → Obtener página de producto
             → Extraer datos → Comparar datos → ¿Datos cambiaron? → Generar alerta / Sin cambio

Ruta de webhook:

Copy
Disparador webhook → Resolver Turnstile → Obtener página de producto
                   → Extraer datos → Comparar datos → ¿Datos cambiaron? → Generar alerta / Sin cambio
                   → Responder al webhook

Comportamientos clave:

  • La operación de CapSolver es Cloudflare Turnstile (no Cloudflare Challenge) — usa AntiTurnstileTaskProxyLess internamente
  • El token se envía como cabecera cf-turnstile-response en la solicitud de obtención
  • No se necesita conversión de formato de proxy — las soluciones Turnstile no utilizan proxies
  • Nodo HTTP Request estándar de n8n — no se requiere servidor TLS dado que no hay cookie cf_clearance para verificación de huella digital
  • La misma lógica de comparación de precios que la versión Challenge: $workflow.staticData.lastPrice para persistencia entre ejecuciones
  • La ruta webhook lee websiteURL y websiteKey directamente del cuerpo POST (no se necesita nodo Set Target Config)
Click para expandir el JSON del flujo de trabajo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Turnstile Scraping \u2014 Price & Product Monitor\n\n### How it works\n\n1. The workflow is triggered every 6 hours or via a webhook.\n2. Target configuration is set including website URL and key.\n3. The Turnstile captcha is solved to access the product page.\n4. Product data is fetched and relevant data is extracted.\n5. Extracted data is compared to previous data to identify changes.\n6. Alerts are generated if there are changes, and a response is sent for webhook requests.\n\n### Setup steps\n\n- [ ] Configure schedule trigger interval as required.\n- [ ] Set up webhook URL for real-time triggering.\n- [ ] Ensure credentials for captcha solver are valid.\n- [ ] Configure target website URL and product key.\n\n### Customization\n\nThe interval for the schedule trigger can be adjusted based on monitoring needs.",
        "width": 480,
        "height": 896
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1184,
        -240
      ],
      "id": "1824aba9-471e-4052-912e-888d939349df",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Scheduled scraping trigger\n\nInitiates the workflow every 6 hours and sets target configurations.",
        "width": 512,
        "height": 304,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -624,
        -80
      ],
      "id": "89b7d6a2-b4bf-4a7d-8667-ce3f31d2eb92",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Scheduled captcha and fetch\n\nSolves captcha and fetches product page data based on schedule.",
        "width": 1472,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        16,
        -64
      ],
      "id": "f39fc94b-d0ae-4141-9491-fa82702a72fc",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Schedule data evaluation\n\nCompares newly fetched data to previous for changes and builds alerts.",
        "width": 240,
        "height": 528,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1536,
        -240
      ],
      "id": "0d31be95-f94c-40fd-99cd-7d988109d3f4",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Webhook scraping trigger\n\nTriggers the scraping workflow via a webhook for real-time updates.",
        "width": 240,
        "height": 352,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -624,
        256
      ],
      "id": "e4a50f47-180c-484e-bf70-09a46543e01b",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "content": "## Webhook captcha and fetch\n\nSolves captcha and fetches product page data for webhook trigger.",
        "width": 1472,
        "height": 272,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        16,
        336
      ],
      "id": "1167cf0e-d561-40fa-b366-331a20d99a31",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "content": "## Webhook data evaluation and response\n\nEvaluates data changes and responds to the webhook with results.",
        "width": 672,
        "height": 432,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1536,
        320
      ],
      "id": "0dab95b9-d762-43cf-9a11-b92212a8c2a7",
      "name": "Sticky Note6"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -576,
        48
      ],
      "id": "ts-s-01",
      "name": "Cada 6 Horas"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-001",
              "name": "websiteURL",
              "value": "https://YOUR-TARGET-SITE.com/product-page",
              "type": "string"
            },
            {
              "id": "cfg-002",
              "name": "websiteKey",
              "value": "YOUR_SITE_KEY_HERE",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -256,
        48
      ],
      "id": "ts-s-02",
      "name": "Configurar Destino [Programación]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Turnstile",
        "websiteURL": "={{ $json.websiteURL }}",
        "websiteKey": "={{ $json.websiteKey }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        64,
        48
      ],
      "id": "ts-s-03",
      "name": "Resolver Turnstile",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "url": "={{ $('Set Target Config [Schedule]').first().json.websiteURL }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "user-agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/125.0.0.0 Safari/537.36"
            },
            {
              "name": "cf-turnstile-response",
              "value": "={{ $json.data.solution.token }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {}
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        384,
        48
      ],
      "id": "ts-s-04",
      "name": "Obtener página de producto"
    },
    {
      "parameters": {
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "price",
              "cssSelector": ".product-price, [data-price], .price"
            },
            {
              "key": "productName",
              "cssSelector": "h1, .product-title"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        704,
        48
      ],
      "id": "ts-s-05",
      "name": "Extraer Datos"
    },
    {
      "parameters": {
        "jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Producto';\nconst parsePrice = (str) => { if (!str) return null; const match = str.match(/[\\d]+\\.?\\d*/); return match ? parseFloat(match[0].replace(',', '')) : null; };\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'bajó' : 'subió') : 'sin cambios';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\nreturn [{ json: { productName, currentPrice, previousPrice: previousPrice || 'primera revisión', changed, direction, diff: changed ? `$${diff}` : null, checkedAt: new Date().toISOString() } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1072,
        48
      ],
      "id": "ts-s-06",
      "name": "Comparar Datos"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "if-1",
              "leftValue": "={{ $json.changed }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1344,
        48
      ],
      "id": "ts-s-07",
      "name": "¿Datos cambiaron?"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "alert",
              "value": "=Precio {{ $json.direction }} para {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }}",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "severity",
              "value": "={{ $json.direction === 'bajó' ? 'deal' : 'info' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1584,
        -48
      ],
      "id": "ts-s-08",
      "name": "Generar Alerta"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "n1",
              "name": "status",
              "value": "sin_cambios",
              "type": "string"
            },
            {
              "id": "n2",
              "name": "currentPrice",
              "value": "={{ $json.currentPrice }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1584,
        128
      ],
      "id": "ts-s-09",
      "name": "Sin Cambios"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "price-monitor-turnstile",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -576,
        448
      ],
      "id": "ts-s-10",
      "name": "Disparador Webhook",
      "webhookId": "6a4f76c7-fc5c-440d-96cb-75c9c3bebcdb",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "Cloudflare Turnstile",
        "websiteURL": "={{ $json.body.websiteURL }}",
        "websiteKey": "={{ $json.body.websiteKey }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        64,
        448
      ],
      "id": "ts-s-11",
      "name": "Resolver Turnstile [W]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "url": "={{ $('Webhook Trigger').item.json.body.websiteURL }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "user-agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/125.0.0.0 Safari/537.36"
            },
            {
              "name": "cf-turnstile-response",
              "value": "={{ $json.data.solution.token }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {}
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        384,
        448
      ],
      "id": "ts-s-12",
      "name": "Obtener página de producto [W]"
    },
    {
      "parameters": {
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "price",
              "cssSelector": ".product-price, [data-price], .price"
            },
            {
              "key": "productName",
              "cssSelector": "h1, .product-title"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        704,
        448
      ],
      "id": "ts-s-13",
      "name": "Extraer Datos [W]"
    },
    {
      "parameters": {
        "jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Producto';\nconst parsePrice = (str) => { if (!str) return null; const match = str.match(/[\\d]+\\.?\\d*/); return match ? parseFloat(match[0].replace(',', '')) : null; };\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'bajó' : 'subió') : 'sin cambios';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\nreturn [{ json: { productName, currentPrice, previousPrice: previousPrice || 'primera revisión', changed, direction, diff: changed ? `$${diff}` : null, checkedAt: new Date().toISOString() } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        448
      ],
      "id": "ts-s-14",
      "name": "Comparar Datos [W]"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "if-2",
              "leftValue": "={{ $json.changed }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1344,
        448
      ],
      "id": "ts-s-15",
      "name": "¿Datos cambiaron? [W]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a4",
              "name": "alert",
              "value": "=Precio {{ $json.direction }} para {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }}",
              "type": "string"
            },
            {
              "id": "a5",
              "name": "severity",
              "value": "={{ $json.direction === 'bajó' ? 'deal' : 'info' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1584,
        432
      ],
      "id": "ts-s-16",
      "name": "Generar Alerta [W]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "n4",
              "name": "status",
              "value": "sin_cambios",
              "type": "string"
            },
            {
              "id": "n5",
              "name": "currentPrice",
              "value": "={{ $json.currentPrice }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1584,
        592
      ],
      "id": "ts-s-17",
      "name": "Sin Cambios [W]"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        2064,
        544
      ],
      "id": "ts-s-18",
      "name": "Responder al Webhook"
    }
  ],
  "connections": {
    "Every 6 Hours": {
      "main": [
        [
          {
            "node": "Set Target Config [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Target Config [Schedule]": {
      "main": [
        [
          {
            "node": "Solve Turnstile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Turnstile": {
      "main": [
        [
          {
            "node": "Fetch Product Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Product Page": {
      "main": [
        [
          {
            "node": "Extract Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Data": {
      "main": [
        [
          {
            "node": "Compare Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Data": {
      "main": [
        [
          {
            "node": "Data Changed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Changed?": {
      "main": [
        [
          {
            "node": "Build Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Change",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Solve Turnstile [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Turnstile [W]": {
      "main": [
        [
          {
            "node": "Fetch Product Page [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Product Page [W]": {
      "main": [
        [
          {
            "node": "Extract Data [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Data [W]": {
      "main": [
        [
          {
            "node": "Compare Data [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Data [W]": {
      "main": [
        [
          {
            "node": "Data Changed? [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Changed? [W]": {
      "main": [
        [
          {
            "node": "Build Alert [W]",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Change [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Alert [W]": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No Change [W]": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}
### Ejemplo 5: Inicio de sesión en cuenta Turnstile

Este flujo de trabajo automatiza el inicio de sesión en un sitio protegido por Turnstile. Sigue el mismo patrón de activación dual (programación + webhook) que el inicio de sesión con cuenta Cloudflare Challenge mencionado anteriormente, pero sin servidor TLS, proxy ni nodo de código personalizado.

Diferencia clave respecto al inicio de sesión con Cloudflare Challenge: No hay servidor TLS, ni proxy, ni nodo de código Prepare TLS Login Request. El token Turnstile se envía como un campo de formulario cf-turnstile-response directamente a través del nodo HTTP Request incorporado en n8n. Las credenciales se envían como parámetros habituales en el cuerpo con formato form-urlencoded.

Ruta de programación:

Copy
Cada 24 horas → Configurar inicio de sesión → Resolver Turnstile → Enviar inicio de sesión
              → ¿Inicio de sesión OK? → Marcar éxito / Marcar fallo

Ruta de webhook:

Copy
Disparador de webhook → Resolver Turnstile → Enviar inicio de sesión
                      → ¿Inicio de sesión OK? → Marcar éxito / Marcar fallo → Responder al webhook

Comportamientos clave:

  • La operación de CapSolver es Cloudflare Turnstile (no Cloudflare Challenge) — usa AntiTurnstileTaskProxyLess, no se requiere proxy
  • El token se envía como un campo de formulario cf-turnstile-response en el cuerpo POST (no en un encabezado cookie como en CF Challenge)
  • No se necesita nodo de código personalizado — el nodo HTTP Request maneja directamente la sumisión form-urlencoded con los campos email, password y cf-turnstile-response
  • ¿Inicio de sesión OK? verifica que statusCode < 400 Y que successMarker esté en el cuerpo de la respuesta — mismo patrón que el inicio de sesión CF Challenge
  • La ruta de programación usa credenciales codificadas en Set Login Config; la ruta de webhook lee usernameValue, passwordValue, usernameField, passwordField, loginActionURL, successMarker del cuerpo POST
  • La ruta de webhook soporta nombres de campos dinámicos — el parámetro name de cada campo de formulario es una expresión ($('Webhook Trigger').item.json.body.usernameField || 'email'), por lo que los llamadores pueden especificar los nombres de campo de su sitio
Click para expandir el JSON del flujo de trabajo
json Copy
{
  "nodes": [
    {
      "parameters": {
        "content": "## Inicio de sesión en cuenta Turnstile\n\n### Cómo funciona\n\n1. Dispara un proceso de inicio de sesión cada 24 horas usando una programación.\n2. Configura la configuración de inicio de sesión y resuelve los desafíos Turnstile.\n3. Envía el formulario de inicio de sesión y verifica si fue exitoso.\n4. Marca el intento de inicio de sesión como éxito o fallo en el flujo de programación.\n5. Alternativamente, inicia un proceso de inicio de sesión mediante un disparador webhook y ejecuta pasos similares para inicios de sesión basados en webhook.\n\n### Pasos de configuración\n\n- [ ] Programar una ejecución periódica configurando un programador.\n- [ ] Configurar el endpoint webhook para disparadores externos.\n- [ ] Establecer las credenciales de inicio de sesión y configuraciones de URL.\n\n### Personalización\n\nAjuste el tiempo en el programador o modifique el manejo de respuestas del webhook según sea necesario.",
        "width": 480,
        "height": 896
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1168,
        -160
      ],
      "id": "8ca80c21-2de5-41e7-b3e4-de184fc1d8fe",
      "name": "Nota adhesiva"
    },
    {
      "parameters": {
        "content": "## Flujo de inicio de sesión programado\n\nDispara cada 24 horas para realizar los pasos de inicio de sesión",
        "width": 1920,
        "height": 448,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -608,
        -160
      ],
      "id": "533be07a-6fe7-4ada-a40c-7a7749ba968d",
      "name": "Nota adhesiva1"
    },
    {
      "parameters": {
        "content": "## Flujo de inicio de sesión vía webhook\n\nManeja solicitudes de inicio de sesión disparadas por un webhook",
        "width": 2288,
        "height": 416,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -608,
        320
      ],
      "id": "c6a8e296-2bfb-4ffa-8819-aa420e936589",
      "name": "Nota adhesiva2"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -560,
        48
      ],
      "id": "ts-l-01",
      "name": "Cada 24 horas"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "l1",
              "name": "websiteURL",
              "value": "https://YOUR-LOGIN-PAGE.com",
              "type": "string"
            },
            {
              "id": "l2",
              "name": "websiteKey",
              "value": "YOUR_SITE_KEY_HERE",
              "type": "string"
            },
            {
              "id": "l3",
              "name": "successMarker",
              "value": "account-dashboard",
              "type": "string"
            },
            {
              "id": "l4",
              "name": "userAgent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/125.0.0.0 Safari/537.36",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -240,
        48
      ],
      "id": "ts-l-02",
      "name": "Configurar inicio de sesión [Programación]"
    },
    {
      "parameters": {
        "operation": "Cloudflare Turnstile",
        "websiteURL": "={{ $json.websiteURL }}",
        "websiteKey": "={{ $json.websiteKey }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        256,
        48
      ],
      "id": "ts-l-03",
      "name": "Resolver Turnstile [Programación]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Set Login Config [Schedule]').item.json.websiteURL }}/login",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "content-type",
              "value": "application/x-www-form-urlencoded"
            },
            {
              "name": "user-agent",
              "value": "={{ $('Set Login Config [Schedule]').item.json.userAgent }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "email",
              "value": "your-email@example.com"
            },
            {
              "name": "password",
              "value": "YOUR_ACCOUNT_PASSWORD"
            },
            {
              "name": "cf-turnstile-response",
              "value": "={{ $json.data.solution.token }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        576,
        48
      ],
      "id": "ts-l-04",
      "name": "Enviar inicio de sesión [Programación]"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "lif1",
              "leftValue": "={{ $json.statusCode < 400 && String($json.body || $json.data || '').includes($('Set Login Config [Schedule]').item.json.successMarker) }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        896,
        48
      ],
      "id": "ts-l-05",
      "name": "¿Inicio de sesión OK? [Programación]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "s1",
              "name": "status",
              "value": "success",
              "type": "string"
            },
            {
              "id": "s2",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1168,
        -48
      ],
      "id": "ts-l-06",
      "name": "Marcar éxito"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f1",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "f2",
              "name": "statusCode",
              "value": "={{ $json.statusCode }}",
              "type": "number"
            },
            {
              "id": "f3",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1168,
        128
      ],
      "id": "ts-l-07",
      "name": "Marcar fallo"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "account-login-turnstile",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -560,
        544
      ],
      "id": "ts-l-08",
      "name": "Disparador webhook",
      "webhookId": "9c7a53a4-d3ee-495b-9381-3a9425bb1b36"
    },
    {
      "parameters": {
        "operation": "Cloudflare Turnstile",
        "websiteURL": "={{ $json.body.websiteURL }}",
        "websiteKey": "={{ $json.body.websiteKey }}",
        "optional": {}
      },
      "type": "n8n-nodes-capsolver.capSolver",
      "typeVersion": 1,
      "position": [
        256,
        544
      ],
      "id": "ts-l-09",
      "name": "Resolver Turnstile [Webhook]",
      "credentials": {
        "capSolverApi": {
          "id": "BeBFMAsySMsMGeE9",
          "name": "Cuenta CapSolver"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Webhook Trigger').item.json.body.loginActionURL }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "content-type",
              "value": "application/x-www-form-urlencoded"
            },
            {
              "name": "user-agent",
              "value": "={{ $('Webhook Trigger').item.json.body.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/125.0.0.0 Safari/537.36' }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "={{ $('Webhook Trigger').item.json.body.usernameField || 'email' }}",
              "value": "={{ $('Webhook Trigger').item.json.body.usernameValue }}"
            },
            {
              "name": "={{ $('Webhook Trigger').item.json.body.passwordField || 'password' }}",
              "value": "={{ $('Webhook Trigger').item.json.body.passwordValue }}"
            },
            {
              "name": "cf-turnstile-response",
              "value": "={{ $json.data.solution.token }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        576,
        544
      ],
      "id": "ts-l-10",
      "name": "Enviar inicio de sesión [Webhook]"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "lif2",
              "leftValue": "={{ $json.statusCode < 400 && String($json.body || $json.data || '').includes($('Webhook Trigger').item.json.body.successMarker) }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        896,
        544
      ],
      "id": "ts-l-11",
      "name": "¿Inicio de sesión OK? [Webhook]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "ws1",
              "name": "status",
              "value": "success",
              "type": "string"
            },
            {
              "id": "ws2",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1168,
        432
      ],
      "id": "ts-l-12",
      "name": "Marcar éxito [W]"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "wf1",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "wf2",
              "name": "statusCode",
              "value": "={{ $json.statusCode }}",
              "type": "number"
            },
            {
              "id": "wf3",
              "name": "checkedAt",
              "value": "={{ new Date().toISOString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1168,
        576
      ],
      "id": "ts-l-13",
      "name": "Marcar fallo [W]"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1536,
        544
      ],
      "id": "ts-l-14",
      "name": "Responder al webhook"
    }
  ],
  "connections": {
    "Every 24 Hours": {
      "main": [
        [
          {
            "node": "Set Login Config [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Login Config [Schedule]": {
      "main": [
        [
          {
            "node": "Solve Turnstile [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Turnstile [Schedule]": {
      "main": [
        [
          {
            "node": "Submit Login [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit Login [Schedule]": {
      "main": [
        [
          {
            "node": "Login OK? [Schedule]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Login OK? [Schedule]": {
      "main": [
        [
          {
            "node": "Mark Success",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Solve Turnstile [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Solve Turnstile [Webhook]": {
      "main": [
        [
          {
            "node": "Submit Login [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit Login [Webhook]": {
      "main": [
        [
          {
            "node": "Login OK? [Webhook]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Login OK? [Webhook]": {
      "main": [
        [
          {
            "node": "Mark Success [W]",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Mark Failed [W]",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark Success [W]": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark Failed [W]": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
  }
}
## Conclusión

Has construido una canalización completa para evitar el desafío de Cloudflare en n8n — sin automatización de navegador, sin Puppeteer, sin Playwright. Solo tres componentes trabajando juntos: CapSolver para resolver el desafío, un servidor TLS en Go para falsificar la huella digital de red de Chrome, y un flujo de trabajo n8n para orquestar todo.

La idea clave es que resolver el desafío es solo la mitad del problema. Sin hacer coincidir las huellas TLS en la siguiente solicitud fetch, cf_clearance es inútil — Cloudflare inspecciona el handshake, no solo la cookie. El servidor TLS httpcloak maneja esa capa, haciendo que la solicitud sea indistinguible de un navegador Chrome real a nivel de red.

El repositorio ahora te ofrece plantillas iniciales reales para sitios protegidos por Cloudflare:

  • Challenge Solver API — expone un webhook que devuelve cf_clearance en bruto + userAgent para aplicaciones externas
  • Scraping — extrae datos de precios y productos de páginas protegidas por Cloudflare, con detección de cambios
  • Inicio de sesión en cuenta — inicia sesión en tu propia cuenta detrás de Cloudflare
  • Turnstile Solver API — un webhook de 3 nodos que devuelve un token Turnstile (sin proxy, sin servidor TLS)
  • Turnstile Scraping — monitorización de precios en páginas protegidas por Turnstile usando el nodo HTTP Request incorporado de n8n
  • Turnstile Account Login — inicia sesión en sitios protegidos por Turnstile con credenciales form-urlencoded y un token resuelto

La API Solver es el punto de entrada más simple — 4 nodos, no se necesita servidor TLS. Los flujos de trabajo Turnstile son aún más simples — no se requiere proxy ni servidor TLS en absoluto, ya que Turnstile devuelve un token (no una cookie ligada a IP) y no hace huellas TLS. Para los flujos de trabajo del desafío Cloudflare que recuperan páginas directamente, CapSolver resuelve el desafío y el servidor TLS hace la solicitud real. Configura los marcadores, mantén los flujos de trabajo inactivos hasta que coincidan con tu objetivo, luego actívalos.

Consejo: Estos flujos de trabajo usan activadores Schedule + Webhook, pero puedes cambiar el nodo de activación a cualquier disparador de n8n — manual, evento de app, envío de formulario, etc. Después de obtener los datos, usa los nodos incorporados de n8n para guardar resultados en Google Sheets, bases de datos, almacenamiento en la nube, o enviar alertas vía Telegram/Slack/Email.


¿Listo para comenzar? Regístrate en CapSolver y usa el código de bonificación n8n para un 8% extra en tu primer recargo.

Banner del código de bonificación de CapSolver

Preguntas Frecuentes

¿Qué es AntiCloudflareTask y cómo es diferente de AntiTurnstileTaskProxyLess?

AntiCloudflareTask resuelve el desafío completo de gestión de bots de Cloudflare en toda la página — la pantalla de "Just a moment…" que bloquea el acceso al sitio entero. Requiere un proxy porque CapSolver debe cargar la página protegida real a través de un navegador. AntiTurnstileTaskProxyLess resuelve widgets Turnstile incrustados dentro de páginas (formularios de inicio de sesión, formularios de registro) y no requiere proxy. Diferentes desafíos, diferentes tipos de tarea.

¿Por qué no puedo simplemente usar el nodo HTTP Request incorporado en n8n para obtener la página?

El nodo HTTP Request de n8n usa la librería estándar net/http de Go, que tiene una huella TLS distinta que Cloudflare detecta. Incluso con una cookie válida cf_clearance, Cloudflare volverá a desafiar cualquier solicitud que no coincida con el perfil TLS de un navegador conocido. El servidor TLS resuelve esto usando httpcloak para falsificar la pila TLS de Chrome real.

¿Por qué mi proxy de centro de datos sigue fallando?

La puntuación de bots de Cloudflare asigna puntajes de riesgo a direcciones IP. Las IP de centros de datos (de AWS, GCP, proveedores VPS, etc.) son bien conocidas y obtienen puntajes de riesgo altos. AntiCloudflareTask usa tu proxy para cargar la página del desafío, y si Cloudflare detecta la IP como de centro de datos, entrega un desafío más difícil que CapSolver no puede resolver, o falla el desafío completamente. Las IP residenciales y móviles tienen puntajes de riesgo menores y pasan de forma más confiable.

¿Los flujos de trabajo Turnstile necesitan servidor TLS o un proxy?

No. Turnstile es fundamentalmente distinto del desafío Cloudflare. Turnstile devuelve un token de corta duración que se envía como cabecera o campo de formulario — no está ligado a una IP específica ni a una huella TLS. CapSolver resuelve Turnstile usando AntiTurnstileTaskProxyLess, que no requiere proxy. Y como envías un token (no una cookie ligada a IP), el nodo HTTP Request incorporado de n8n funciona perfectamente — no se necesita falsificación de huella TLS.

¿cf_clearance expira?

Sí. La expiración depende de la configuración de Cloudflare del sitio — puede variar desde unos minutos hasta 24 horas. Para trabajos de scraping recurrentes, el flujo programado (cada 6 horas) vuelve a resolver el desafío regularmente. Para scraping bajo demanda, el path del webhook resuelve un desafío nuevo en cada solicitud.

Ver más

Web ScrapingApr 22, 2026

Arquitectura de raspado de web para extracción de datos escalable

Aprende una arquitectura de raspado web escalable en Rust con reqwest, scraper, raspado asíncrono, raspado con navegador sin cabeza, rotación de proxies y manejo de CAPTCHA conforme.

Aloísio Vítor
Aloísio Vítor
Web ScrapingFeb 17, 2026

Cómo resolver Captcha en Nanobot con CapSolver

Automatiza la resolución de CAPTCHA con Nanobot y CapSolver. Utiliza Playwright para resolver reCAPTCHA y Cloudflare autónomamente.

Adélia Cruz
Adélia Cruz
Web ScrapingFeb 10, 2026

Datos como Servicio (DaaS): ¿Qué es y por qué es importante en 2026

Comprender Datos como Servicio (DaaS) en 2026. Explora sus beneficios, casos de uso y cómo transforma los negocios con insights en tiempo real y escalabilidad.

Sora Fujimoto
Sora Fujimoto
Web ScrapingFeb 05, 2026

Cómo arreglar errores comunes de raspado de web en 2026

Dominar la resolución de diversos errores de scrapers web como 400, 401, 402, 403, 429, 5xx y Cloudflare 1001 en 2026. Aprender estrategias avanzadas para la rotación de IPs, encabezados y limitación de tasa adaptativa con CapSolver.

Emma Foster
Emma Foster

Contenido

Blog
Web Scraping