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:
- Recebe a sitekey (chave pública do widget) e a URL da página como entrada
- Envia a tarefa CAPTCHA para a CaptchaAI
- Consulta o resultado até o token ficar pronto
- 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 Resources → Create New → REST 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:userrecaptchagooglekey:{{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:getid:{{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:
- Como resolver o callback do reCAPTCHA v2 pela API
- reCAPTCHA v2 e Turnstile no mesmo site
- Como funciona o mecanismo de callback do reCAPTCHA v2
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: