Retool не умеет решать CAPTCHA сам: нужен внешний сервис, который принимает sitekey и pageurl, а возвращает токен. Ниже — схема на CaptchaAI штатными средствами Retool: один REST-ресурс, два запроса к API, JavaScript-запрос с циклом опроса и только потом интерфейс. На выходе приложение отдаёт готовый g-recaptcha-response для отправки формы.
Сценарий: заявки на визовый портал из внутренней панели
Типичная ситуация в агентстве из Алматы или Минска: операционная команда ведёт заявки клиентов в панели Retool, а подаёт их на портал с reCAPTCHA v2 — и оператор десятки раз за смену уходит решать проверку вручную. С этой интеграцией он остаётся в одном окне.
Работайте только с порталами, где у вас есть право подавать данные от имени клиента, и собирайте лишь те персональные данные, которые вправе обрабатывать (для читателей из РФ — с оглядкой на 152-ФЗ «О персональных данных»).
Шаг 1: заведите REST-ресурс CaptchaAI
В Retool откройте Resources → Create New → REST API:
| Поле | Значение |
|---|---|
| Name | CaptchaAI |
| Base URL | https://ocr.captchaai.com |
| Authentication | нет — API-ключ передаётся параметром запроса |
Сохраните ресурс: на него будут ссылаться оба следующих запроса.
Шаг 2: запрос отправки задачи
Создайте запрос submitCaptcha — метод GET, путь /in.php, ресурс CaptchaAI. Параметры:
| Ключ | Значение |
|---|---|
key |
{{secretsStore.CAPTCHAAI_API_KEY}} |
method |
userrecaptcha |
googlekey |
{{sitekeyInput.value}} |
pageurl |
{{pageurlInput.value}} |
json |
1 |
API-ключ храните в Secrets Store (Settings → Secrets): определения запросов видны всем, у кого есть доступ к редактору. Без json: 1 эндпоинт вернёт текст, а не JSON.
Трансформер (необязательный):
// Parse the response
const data = {{ submitCaptcha.data }};
if (data.status === 1) {
return { taskId: data.request, status: 'submitted' };
}
return { error: data.request, status: 'failed' };
Шаг 3: запрос опроса результата
Создайте запрос pollResult — тоже GET, путь /res.php, тот же ресурс.
| Ключ | Значение |
|---|---|
key |
{{secretsStore.CAPTCHAAI_API_KEY}} |
action |
get |
id |
{{submitCaptcha.data.request}} |
json |
1 |
Трансформер:
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 — не ошибка, а промежуточное состояние; опечатка в слове входит в протокол.
Шаг 4: цикл опроса на JavaScript
Запрос solveCaptcha связывает оба предыдущих: отправляет задачу, ждёт 15 секунд и опрашивает результат с интервалом в 5 секунд.
// 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();
Цифры подобраны под лимит Retool: JavaScript-запрос живёт не дольше 120 секунд, а 15 + 20 × 5 укладывается в этот бюджет.
Шаг 5: соберите интерфейс
Ввод. Поля sitekeyInput («reCAPTCHA Sitekey») и pageurlInput («URL страницы»), кнопка solveButton («Решить CAPTCHA») с solveCaptcha.trigger() в onClick.
Статус. Текстовый компонент {{ solveCaptcha.isFetching ? "Solving..." : "" }} и индикатор загрузки при {{ solveCaptcha.isFetching }} — иначе оператор решит, что панель зависла, и нажмёт кнопку второй раз.
Результат. Поле tokenOutput только для чтения со значением {{ solveCaptcha.data?.token || '' }}, кнопка копирования и значок статуса по {{ solveCaptcha.data?.status }}.
Шаг 6: подставьте токен в отправку формы
Создайте запрос submitForm на целевой API: метод POST, в теле — данные формы с полем g-recaptcha-response: {{solveCaptcha.data.token}}. Кнопку «Отправить форму» держите неактивной до {{ solveCaptcha.data?.status === 'solved' }}: токен живёт недолго.
Что ломается чаще всего
| Симптом | Причина | Что сделать |
|---|---|---|
ERROR_WRONG_USER_KEY |
ключ не в Secrets Store или с опечаткой | проверьте Settings → Secrets |
| Вместо JSON приходит текст | потерян параметр json |
добавьте json: 1 |
Polling timeout |
типу CAPTCHA нужно больше времени | увеличьте итерации с 20 до 30 |
submitCaptcha.data — undefined |
отправка ещё не отработала | отправка должна идти до опроса |
| JavaScript-запрос обрывается | лимит Retool в 120 секунд | оставьте 20 итераций по 5 секунд |
Сколько потоков закладывать
Тарификация — по числу одновременных задач (потоков), а не решений: в пределах плана решения не ограничены. Панели с ручным вводом хватает BASIC ($15/мес, 5 потоков) или STANDARD ($30/мес, 15 потоков); ADVANCE ($90/мес, 50 потоков) — для пакетной обработки.
Частые вопросы
Нужен ли отдельный бэкенд между Retool и CaptchaAI?
Нет. И облачный, и self-hosted Retool обращаются к ocr.captchaai.com напрямую — прослойка нужна лишь там, где политика безопасности запрещает исходящие запросы.
Что будет, если оператор нажмёт «Решить CAPTCHA» дважды подряд?
Уйдут две независимые задачи, и обе спишут поток на время решения. Поэтому кнопку стоит блокировать на {{ solveCaptcha.isFetching }}, а не полагаться на внимательность оператора.
Какие типы CAPTCHA поддерживаются, кроме reCAPTCHA v2?
Меняя method в submitCaptcha — reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge, GeeTest v3, текстовые и графические CAPTCHA. CaptchaFox, Friendly Captcha и Lemin — в статусе beta. hCaptcha и FunCaptcha не поддерживаются, GeeTest v4 заявлен как «скоро».
Почему первый опрос стоит откладывать на 15 секунд?
Раньше res.php почти всегда ответит CAPCHA_NOT_READY. Немедленный опрос не ускоряет решение, а лишь расходует итерации, которых из-за лимита немного.
Что делать, если токен не проходит на целевом сайте?
Сверьте sitekey и pageurl с тем, что отдаёт страница: чаще всего расходится домен — токен решён для одного адреса, а уходит на другой. Разбор параметров — в руководстве по reCAPTCHA v2.