Integrations

Retool + CaptchaAI: ferramenta interna para tratamento de formulários CAPTCHA

Dá para resolver reCAPTCHA v2 dentro do Retool sem backend próprio: um recurso REST API apontando para a CaptchaAI, duas queries (envio e consulta) e uma query JavaScript que amarra as duas em um loop de polling. No fim, você tem um token que qualquer outra query do app usa no envio do formulário.

A seguir, o caminho completo: recurso, queries, interface e tratamento de erro.

O que você vai montar

O caso típico: o time de back-office usa um app Retool para enviar dados a um portal externo que sua empresa tem autorização para operar, e o portal pede reCAPTCHA v2 a cada envio. Em vez de resolver na mão, o app faz o ciclo inteiro:

  1. Recebe a sitekey (chave pública do widget) e a URL da página como entrada
  2. Envia a tarefa CAPTCHA para a CaptchaAI
  3. Consulta o resultado até o token ficar pronto
  4. Exibe o token resolvido para uso no envio do formulário

Antes de montar: defina o escopo autorizado

O app deve atender só fluxos autorizados: portais em que sua empresa é cliente, ambientes de staging e testes próprios. Registre usuário, data e URL de cada resolução — e, se o formulário carrega dados de clientes, considere as obrigações da LGPD (RGPD, em Portugal) antes de logar o corpo da requisição.

Chave de API e dimensionamento de threads

A CaptchaAI cobra por thread concorrente, com resoluções ilimitadas dentro das threads do plano. Dimensione pelo número de pessoas que clicam em "Resolver CAPTCHA" ao mesmo tempo:

  • BASIC (US$ 15/mês, 5 threads) — back-office pequeno, uso esporádico.
  • STANDARD (US$ 30/mês, 15 threads) — app compartilhado por vários times.
  • Retool self-hosted em sa-east-1 (São Paulo) — some o tempo de ida e volta ao orçamento do polling.

Passo 1: cadastre a CaptchaAI como recurso REST API

No Retool, vá em ResourcesCreate NewREST API e preencha três campos:

  • Nome: CaptchaAI
  • URL base: https://ocr.captchaai.com
  • Autenticação: nenhuma — a chave de API vai como parâmetro de consulta.

Salve o recurso: as queries seguintes o referenciam pelo nome.

Passo 2: crie a query de envio

Crie uma query chamada submitCaptcha sobre o recurso CaptchaAI, com Action type GET e caminho /in.php. Os parâmetros de consulta são estes:

  • key: {{secretsStore.CAPTCHAAI_API_KEY}}
  • method: userrecaptcha
  • googlekey: {{sitekeyInput.value}}
  • pageurl: {{pageurlInput.value}}
  • json: 1

Guarde a chave no Secrets Store do Retool (Settings → Secrets), fora da definição da query.

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' };

Passo 3: crie a query de consulta do resultado

Crie pollResult sobre o mesmo recurso, também com Action type GET, apontando para /res.php. Mudam só os parâmetros:

  • key: {{secretsStore.CAPTCHAAI_API_KEY}}
  • action: get
  • id: {{submitCaptcha.data.request}}
  • json: 1

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' };

CAPCHA_NOT_READY não é erro: é a resposta normal enquanto a tarefa está na fila. Trate como falha só o que vier diferente disso.

Passo 4: monte o loop de polling em JavaScript

Crie uma query JavaScript chamada solveCaptcha para orquestrar envio e consultas:

// 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();

Os dois números do loop não são arbitrários:

  • Antes de 15 s a resposta quase sempre é CAPCHA_NOT_READY, então a espera inicial economiza requisições.
  • 20 iterações a cada 5 s cabem no limite de 120 s do Retool para queries JS.

Passo 5: monte a interface do app

Um layout enxuto basta: dois campos, um botão, um sinal de progresso e a caixa do token.

  • Text Input (sitekeyInput): rótulo "reCAPTCHA sitekey".
  • Text Input (pageurlInput): rótulo "URL da página".
  • Button (solveButton): rótulo "Resolver CAPTCHA", onClick → solveCaptcha.trigger().
  • Text de status: {{ solveCaptcha.isFetching ? "Solving..." : "" }}, com indicador de carregamento visível quando {{ solveCaptcha.isFetching }}.
  • Text Area (tokenOutput), somente leitura, rótulo "Token resolvido": {{ solveCaptcha.data?.token || '' }}.
  • Botão de copiar e badge de status, este conforme {{ solveCaptcha.data?.status }}.

Mantenha o indicador de carregamento visível: sem feedback, o operador clica duas vezes e gasta duas threads.

Passo 6: use o token na requisição final

Com o token em mãos, outra query faz o envio. Crie submitForm apontando para a API de destino, com Action type POST e o corpo do formulário incluindo g-recaptcha-response: {{solveCaptcha.data.token}}.

Ligue essa query a um botão "Enviar formulário" habilitado apenas quando {{ solveCaptcha.data?.status === 'solved' }}. O token do reCAPTCHA v2 tem validade curta: envie o formulário em seguida.

Erros comuns e como corrigir

Sintoma Causa Correção
ERROR_WRONG_USER_KEY Chave ausente ou errada no Secrets Store Confira em Settings → Secrets
A query devolve texto puro, não JSON Falta o parâmetro json=1 Adicione json: 1 à query
O polling estoura o tempo limite O tipo de CAPTCHA exige mais tempo Aumente as iterações de 20 para 30
submitCaptcha.data vem indefinido O envio ainda não rodou Execute o envio antes da consulta
Timeout da query JavaScript Limite de 120 s do Retool para queries JS Mantenha 20 iterações com intervalos de 5 s

Se o mesmo erro se repetir em série, pare o app e confira o saldo antes de gastar threads.

Perguntas frequentes

O que os times mais perguntam depois de colocar o app em uso:

Quanto tempo o app leva para devolver o token?

Depende do tipo de desafio. No reCAPTCHA v2, a primeira consulta útil costuma vir poucos segundos após a espera inicial de 15 s — meça no seu próprio ambiente.

E se duas pessoas clicarem em "Resolver CAPTCHA" ao mesmo tempo?

Cada clique ocupa uma thread. No BASIC, cinco resoluções simultâneas rodam sem fila e a sexta espera uma liberar. Se vários times usam o app, suba de plano em vez de serializar os cliques.

Posso usar o mesmo app para Cloudflare Turnstile?

Sim — troque o method e os parâmetros do envio; o polling continua igual. A CaptchaAI resolve reCAPTCHA v2/v3, Cloudflare Turnstile e Challenge, GeeTest v3 e CAPTCHAs de imagem e de grade, além de CaptchaFox (beta), Friendly Captcha (beta) e Lemin (beta). hCaptcha e FunCaptcha não são suportados; o GeeTest v4 segue como "em breve".

Como o app deve se comportar quando a resolução falha?

Trate status: 'error' e status: 'timeout' de formas diferentes: o erro devolve um código da API (chave errada, saldo insuficiente) e pede ação humana; o timeout só pede nova tentativa. Em ambos, o botão de envio fica desabilitado.

Artigos relacionados

Para entender o que acontece do lado do widget depois que o token volta:

Próximas etapas

Leve a resolução de CAPTCHA às ferramentas internas do time: obtenha sua chave de API da CaptchaAI e cadastre o recurso REST. Se a sua automação vive em outra plataforma no-code, o roteiro é o mesmo:

Os comentários estão desativados para este artigo.