Integraciones

Retool + CaptchaAI: manejo de CAPTCHA en herramientas internas

Retool no trae ningún componente que resuelva un CAPTCHA, y no hace falta: bastan dos consultas de API REST y una de JavaScript que las coordina. Envías el desafío a CaptchaAI desde in.php, consultas el resultado en res.php y el token queda en una variable que inyectas en el formulario. Esta guía recorre esa implementación con reCAPTCHA v2, paso a paso.

Por qué esto aparece justo en las herramientas internas

Casos típicos en la región: una gestoría en México que registra decenas de expedientes en un portal de trámites, o un equipo de soporte en España que consulta solicitudes en un portal de cita previa. El flujo entero vive en Retool; solo el reCAPTCHA v2 del último paso obliga a resolverlo a mano.

Dentro de la app Qué hace
Entrada Recibe el sitekey y la URL de la página
Envío Manda el desafío a CaptchaAI (in.php)
Sondeo Consulta el resultado hasta que llega (res.php)
Salida Expone el token para el envío del formulario

Antes de automatizar contra un portal de terceros, revisa sus términos de servicio y la normativa de protección de datos aplicable (RGPD y LOPDGDD en España, LFPDPPP en México). Trabaja solo sobre flujos autorizados.

Paso 1: da de alta CaptchaAI como recurso de API REST

En Retool, ve a ResourcesCreate NewREST API:

Campo Valor
Nombre CaptchaAI
Base URL https://ocr.captchaai.com
Autenticación Ninguna (la clave API viaja como parámetro de consulta)

Guarda el recurso: lo referenciarás desde todas las consultas.

Paso 2: la consulta de envío (in.php)

Crea una consulta submitCaptcha:

Ajuste Valor
Resource CaptchaAI
Action Type GET
URL Path /in.php

Parámetros de consulta:

Clave Valor
key {{secretsStore.CAPTCHAAI_API_KEY}}
method userrecaptcha
googlekey {{sitekeyInput.value}}
pageurl {{pageurlInput.value}}
json 1

Guarda la clave en el Secrets Store de Retool (Settings → Secrets): así no queda escrita en la definición de la consulta, que cualquier editor puede abrir.

Con otro tipo solo cambia el method: turnstile para Cloudflare Turnstile, geetest para GeeTest v3, post para CAPTCHA de imagen.

Transformador (opcional):

// Parse the response
const data = {{ submitCaptcha.data }};
if (data.status === 1) {
  return { taskId: data.request, status: 'submitted' };
}
return { error: data.request, status: 'failed' };

Paso 3: la consulta de sondeo (res.php)

Crea una consulta llamada pollResult:

Ajuste Valor
Resource CaptchaAI
Action Type GET
URL Path /res.php

Parámetros de consulta:

Clave Valor
key {{secretsStore.CAPTCHAAI_API_KEY}}
action get
id {{submitCaptcha.data.request}}
json 1

El transformador reduce las tres respuestas posibles a un estado que la interfaz entiende: resuelto, pendiente o error.

Transformador:

const data = {{ pollResult.data }};
if (data.status === 1) {
  return { token: data.request, status: 'solved' };
}
if (data.request === 'CAPCHA_NOT_READY') {
  return { status: 'pending' };
}
return { error: data.request, status: 'error' };

Paso 4: el ciclo de sondeo en una consulta de JavaScript

Las consultas anteriores no se llaman solas. Crea una consulta de JavaScript solveCaptcha que envíe la tarea y sondee hasta obtener el token:

// solveCaptcha — JavaScript Query
async function solve() {
  // Submit the CAPTCHA task
  await submitCaptcha.trigger();
  const submitResult = submitCaptcha.data;

  if (submitResult.status !== 1) {
    return { error: submitResult.request, status: 'submit_failed' };
  }

  const taskId = submitResult.request;

  // Wait 15 seconds before first poll
  await new Promise(r => setTimeout(r, 15000));

  // Poll up to 20 times (100 seconds max)
  for (let i = 0; i < 20; i++) {
    await pollResult.trigger({
      additionalScope: { taskId: taskId }
    });

    const result = pollResult.data;

    if (result.status === 1) {
      return { token: result.request, status: 'solved' };
    }

    if (result.request !== 'CAPCHA_NOT_READY') {
      return { error: result.request, status: 'error' };
    }

    // Wait 5 seconds before next poll
    await new Promise(r => setTimeout(r, 5000));
  }

  return { error: 'Polling timeout', status: 'timeout' };
}

return solve();

El techo de 20 iteraciones no es arbitrario: las consultas de JavaScript de Retool se cortan a los 120 segundos y toda la espera cabe ahí.

Paso 5: la interfaz mínima

Con la lógica lista, bastan seis componentes:

Componente Nombre Configuración
Text Input sitekeyInput Etiqueta "reCAPTCHA Sitekey"
Text Input pageurlInput Etiqueta "URL de la página"
Button solveButton Al hacer clic → solveCaptcha.trigger()
Text {{ solveCaptcha.isFetching ? "Solving..." : "" }}
Text Area tokenOutput {{ solveCaptcha.data?.token \|\| '' }}, solo lectura
Badge Éxito o error según {{ solveCaptcha.data?.status }}

Mostrar el token ayuda mientras depuras; en la versión final puedes ocultarlo.

Paso 6: usa el token en la consulta siguiente

El token viaja con el formulario. Crea una consulta submitForm:

Ajuste Valor
Resource Tu API de destino
Action Type POST
Body Datos del formulario, incluyendo g-recaptcha-response: {{solveCaptcha.data.token}}

Conéctala a un botón "Enviar formulario" habilitado solo cuando {{ solveCaptcha.data?.status === 'solved' }}. El token dura poco: enviarlo minutos después suele terminar en rechazo, así que encadena ambos pasos en la misma interacción.

Errores frecuentes y cómo salir de ellos

Problema Causa Solución
ERROR_WRONG_USER_KEY Clave ausente o incorrecta en el Secrets Store Verifícala en Settings → Secrets
Respuesta en texto plano y no JSON Falta el parámetro json=1 Añade json: 1 a los parámetros
Se agota el tiempo de sondeo Ese tipo necesita más tiempo de resolución Sube las iteraciones de 20 a 30
submitCaptcha.data está undefined La consulta aún no se ha ejecutado Ejecuta el envío antes del sondeo
Se corta la consulta de JavaScript Límite de 120 segundos de Retool Deja 20 iteraciones con intervalos de 5 segundos

Qué plan necesita una app interna

CaptchaAI factura por thread concurrente, no por resolución: cada plan incluye resoluciones ilimitadas por thread.

Plan Precio Threads Cuándo encaja
BASIC $15/mes 5 Una app interna de uso interactivo
STANDARD $30/mes 15 Varias apps sobre el mismo recurso
ADVANCE $90/mes 50 Envíos en lote

La escala en USD sigue hasta VIP-3. Para quien factura en moneda volátil, lo que cuenta es un costo mensual fijo, no por resolución.

Preguntas frecuentes

¿Necesito un plan distinto si varias apps de Retool comparten el recurso?

No necesariamente. El límite es de threads concurrentes, no de apps: una sola clave sirve para todas si la suma de resoluciones simultáneas cabe en tus threads. Lo que ocupa un thread:

  • Cada solveCaptcha en vuelo, venga de la app que venga
  • Un sondeo abierto a la espera del token
  • Nada más: al resolverse, la consulta libera el thread

¿Puedo lanzar esto desde un workflow de Retool en vez de un botón?

Sí. Funciona igual dentro de un Retool Workflow, y ahí el límite de 120 segundos aprieta menos: puedes separar envío y sondeo en pasos con sus propios reintentos.

¿Sirve tanto en Retool Cloud como en la versión autoalojada?

En ambas. Las dos admiten recursos de API REST y consultas de JavaScript, y una instancia autoalojada llama a la API directamente, sin proxy.

Siguiente paso

Monta el recurso, pega las consultas y prueba el ciclo con un sitekey de staging: obtén tu clave API de CaptchaAI y la app resolverá su primer reCAPTCHA v2 hoy mismo.

Para seguir leyendo

Los comentarios están deshabilitados para este artículo.