API — Лицензия, доступ и сертификат
Проверяйте статус лицензии, управляйте аутентификацией, читайте общий статус системы и скачивайте корневой сертификат (CA) через API.
Лицензия
/api/v1/license/statusТребуется авторизацияВозвращает информацию о том, активна ли лицензия, вместе с вашим тарифом, правом на пул портов (pool) и лимитами процессов Challenge Breaker. Лимита портов здесь нет: действующий потолок отдаёт поле max_ports у GET /api/v1/status.
curl -H "X-API-Key: YOUR_API_KEY" http://127.0.0.1:8891/api/v1/license/status{
"activated": true,
"email": "demo@blanktrail.pro",
"plan": "pro",
"period_end": "2027-02-15T00:00:00Z",
"pool": true,
"js_solver_max_procs": 4,
"js_solver_procs": 4,
"js_solver_live_procs": 1
}- Поля label и allowed_domains приходят ТОЛЬКО у сервисного (промо) тарифа, поэтому в примере выше их нет: label — приставка к названию тарифа, дашборд показывает её как «Pro (WB)»; allowed_domains — список доменов, которыми этот тариф ограничен, и панель выводит под названием тарифа строку «Ограничено доменами: …».
- 🔴 allowed_domains — не справка, а запрет: пока список непустой, порт пускает ТОЛЬКО на эти домены. Запись без префикса покрывает сам домен и все его поддомены, запись с «=» в начале — только точное имя, голый IP-адрес не совпадает НИКОГДА. Пустой список (поля нет) — ограничения нет.
- 🔴 Отказ приходит с САМОГО прокси-порта, а не из этого API: HTTP CONNECT и обычный HTTP получают 403 Forbidden с пустым телом — без JSON и без поля error; SOCKS5 — код ответа 0x02 «connection not allowed by ruleset»; UDP ASSOCIATE под таким тарифом не выдаётся вовсе. Вышестоящий прокси тут ни при чём — менять выход бесполезно, сверьте адрес со списком.
- Challenge Breaker на домен вне списка не запускается. Ручки API, проверка порта и остальной дашборд работают как обычно.
Аутентификация
Пароль здесь — от дашборда, а не от API: запросы к API удостоверяются ключом в заголовке X-API-Key, и cookie сессии им не нужна. Эти ручки нужны интерфейсу и сценариям первоначальной настройки.
/api/v1/auth/loginБез авторизацииВход в дашборд паролем; ставит cookie сессии.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
password | string | Да | Пароль дашборда. |
{ "password": "your-dashboard-password" }curl -X POST -H "Content-Type: application/json" -c cookies.txt \
-d '{"password":"your-dashboard-password"}' \
http://127.0.0.1:8891/api/v1/auth/login{
"status": "ok"
}- 401 «invalid password»; 429 «too many attempts — try again later» после череды неудач с одного адреса.
- 501 «auth not configured», если вход в дашборд в этой сборке не настроен.
/api/v1/auth/logoutТребуется авторизацияОчищает cookie сессии дашборда.
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/auth/logout{
"status": "ok"
}/api/v1/auth/statusТребуется авторизацияСостояние входа и пароля: вошли ли, задан ли пароль и остался ли он первоначальным.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/auth/status{
"authenticated": true,
"password_is_initial": false,
"password_set": true
}- password_is_initial=true — пароль ещё тот, что выдан при установке; это повод показать требование его сменить.
/api/v1/auth/passwordТребуется авторизацияМеняет пароль дашборда. Нужен текущий пароль.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
current_password | string | Да | Текущий пароль. 🔴 Поле называется current_password, а не current. |
new_password | string | Да | Новый пароль, не короче 8 символов. 🔴 Поле называется new_password, а не new. |
{ "current_password": "old-password", "new_password": "new-password" }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"current_password":"old-password","new_password":"new-password"}' \
http://127.0.0.1:8891/api/v1/auth/password{
"status": "ok"
}- 🔴 Тело с именами current и new декодируется в две пустые строки, и ручка отвечает 401 «current password is incorrect» — как будто ошиблись паролем, а не именем поля.
- 400 «password must be at least 8 characters» на слишком коротком новом пароле.
- 501 «auth not configured», если вход в дашборд в этой сборке не настроен.
/api/v1/auth/apikey/rotateТребуется авторизацияВыдаёт новый ключ API и возвращает его. Прежний перестаёт работать немедленно.
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/auth/apikey/rotate{
"api_key": "a1b2c3d4e5f6a7b8c9d0"
}- Запрос идёт со СТАРЫМ ключом, а ответ несёт новый — другого случая узнать его нет.
- 500 «could not rotate key», если новый ключ не удалось сохранить; 501, если вход не настроен.
Статус системы
/api/v1/statusТребуется авторизацияВозвращает общий статус: версию, число открытых и максимальное число портов (max_ports — действующий потолок, меньшее из настроенного максимума и лимита вашей лицензии), количество идентичностей, время работы и прочее.
curl -H "X-API-Key: YOUR_API_KEY" http://127.0.0.1:8891/api/v1/status{
"version": "1.3.2",
"commit": "a1b2c3d",
"commit_time": "2026-09-01T10:00:00Z",
"open_ports": 2,
"max_ports": 1000,
"idle_timeout_seconds": 1800,
"ports": [
"…"
],
"profile_count": 143900,
"profile_counts": {
"chrome_152+windows": 41000,
"chrome_152+macos": 22000,
"firefox_152+windows": 17400,
"safari_18+ios": 9800
},
"uptime": "3d14h22m",
"domain_routing": {
"domains": [],
"proxy": ""
},
"domain_rules_count": 3,
"pack_loaded": true,
"solver_bundle_state": "ready",
"solver_bundle_total": 1,
"vision_bundle_state": "ready",
"vision_bundle_total": 1,
"font_bundle_state": "ready",
"font_bundle_total": 1,
"build_state": "ok",
"device_bound": true
}{
"pack_loaded": true,
"solver_bundle_state": "downloading",
"vision_bundle_state": "dormant",
"font_bundle_state": "failed",
"font_bundle_reason": "macos: connection refused",
"build_state": "mismatch",
"build_reason": "the program file does not match the published build — please reinstall",
"device_bound": true,
"device_rebound_at": "2026-09-01T10:00:00Z"
}- Первый пример — УСПЕШНЫЙ исход. Поля solver_bundle_state, vision_bundle_state и font_bundle_state принимают одно из четырёх значений: dormant — загрузка ещё ни разу не начиналась (для vision это норма: модели качаются в момент первой встречи с капчей), downloading — идёт скачивание, ready — установлено, failed — сорвалось.
- 🔴 Проверку готовности нельзя писать как «равно ready, всё остальное — поломка»: свежая установка сначала показывает downloading, и такой скрипт не отличит «ещё качается» от «упало».
- У каждого состояния есть парное поле причины — solver_bundle_reason, vision_bundle_reason, font_bundle_reason: короткая строка без секретов. При ready и dormant его в ответе нет. Поле font_bundle_reason перечисляет ВСЕ сбойные ОС (macos: connection refused; windows: 404), потому что само состояние — худшее из пулов по операционным системам.
- 🔴 vision_bundle_state ready вместе с НЕПУСТЫМ vision_bundle_reason — это не «всё хорошо»: бандл скачан, но получить ключ содержимого не удалось (лицензия без права на решатель, устаревший токен, Кабинет недоступен), и reCAPTCHA молча не решается.
- Значение pack_loaded false означает, что боевые рецепты отпечатков не загружены; pack_reason приходит рядом и объясняет почему (not yet attempted, недоступность Кабинета). Пустая причина при false — у лицензии нет права на пак.
- Поле build_state — сверка ФАЙЛА программы с опубликованной сборкой. Значения: ok, mismatch, unregistered. 🔴 Поля может не быть в ответе вовсе — это «сигнала ещё не было», и считать его за ok нельзя. При mismatch и unregistered рядом приходит build_reason с готовым текстом для показа: mismatch — файл испорчен или подменён, нужна переустановка; unregistered — эта сборка не публиковалась сервером лицензий, и переустановка того же выпуска не поможет.
- Значение device_bound false — установка не закреплена за ключом устройства. Поле device_rebound_at (время последней перепривязки) приходит, только если перепривязка была.
/api/v1/healthБез авторизацииПростая проверка работоспособности, которая всегда возвращает OK.
curl http://127.0.0.1:8891/api/v1/health
{
"status": "ok"
}
Без ключа доступны ровно четыре пути: эта проверка, POST /api/v1/auth/login, GET /crl (за списком отзыва идёт стек проверки сертификатов ОС, а не человек) и GET /export/{token}/… (доступ там даёт токен в самом адресе). Всё остальное отвечает 401. Проверкой живости пользуются из мониторинга и из оболочки контейнера; о состоянии портов или лицензии она не сообщает ничего.
Корневой сертификат (CA)
Приложение вскрывает TLS собственным корневым сертификатом, и система должна ему доверять — иначе браузер покажет ошибку сертификата на каждом сайте. Три ручки отдают сертификат в трёх видах, четвёртая ставит его сама.
/api/v1/caТребуется авторизацияОтдаёт корневой сертификат текстом PEM.
curl -H "X-API-Key: YOUR_API_KEY" -o blanktrail-ca.pem \
http://127.0.0.1:8891/api/v1/ca- 404 «CA certificate path not configured», если путь к сертификату не задан; 500 «failed to read CA certificate», если файл не читается. Разбор PEM здесь не делается — это отдаёт файл как есть; проверку «не PEM» делают ручки, которым нужен разобранный сертификат, /ca.crt и /ca.mobileconfig.
/api/v1/ca.crtТребуется авторизацияТот же сертификат в двоичном формате DER — его понимает системный установщик macOS и iOS.
curl -H "X-API-Key: YOUR_API_KEY" -o blanktrail-ca.crt \
http://127.0.0.1:8891/api/v1/ca.crt/api/v1/ca.mobileconfigТребуется авторизацияПрофиль Apple .mobileconfig: открытый на устройстве, он ставит сертификат сам.
curl -H "X-API-Key: YOUR_API_KEY" -o blanktrail.mobileconfig \
http://127.0.0.1:8891/api/v1/ca.mobileconfig- На iPhone мало установить профиль: сертификат нужно ещё ОТМЕТИТЬ доверенным в настройках «Об этом устройстве → Доверие сертификатам».
/api/v1/ca/installТребуется авторизацияСтавит корневой сертификат в пользовательское хранилище доверенных корней.
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/ca/install{
"status": "installed"
}- Только для Windows: на остальных системах отвечает 501, и сертификат нужно скачать и добавить вручную.
- После установки браузер нужно ПОЛНОСТЬЮ перезапустить — закрыть все окна, а не открыть новое: хранилище доверия он читает при старте.
- 500 «install failed: …», если системное хранилище отвергло сертификат.
Challenge Breaker
Эти ручки показывают, чем разбор защит занят прямо сейчас и сколько машины ему разрешено занимать. Доступны они на любом тарифе: без Challenge Breaker счётчики солвов остаются нулевыми, но встреченные защиты всё равно видны — так вы узнаете, с чем столкнулся ваш трафик.
/api/v1/solver/statsТребуется авторизацияСчётчики с момента запуска: попытки и успехи по семействам защит, встреченные защиты и занятые ресурсы.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/solver/stats{
"families": [ { "family": "recaptcha", "attempts": 41, "solved": 38 } ],
"total_attempts": 41,
"total_solved": 38,
"since_start": true,
"resources": { "live_procs": 2, "busy_procs": 1, "cap": 4,
"constrained": false, "reason": "", "last_error": "",
"launch_failures": 0, "proc_deaths": 0, "mem_recycles": 0 },
"seen": { "total": 47, "since_unix_ms": 1757116800000,
"vendors": [ { "vendor": "recaptcha", "count": 41 },
{ "vendor": "turnstile", "count": 6 } ] }
}- 🔴 На тарифе БЕЗ Challenge Breaker непустым остаётся только seen: солвов не было, а защиты были. Ради этого поле и заведено.
/api/v1/solver/queueТребуется авторизацияЖивая очередь: сколько ждёт и сколько решается прямо сейчас, предел очереди и по строке на запрос.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/solver/queue{
"queued": 2,
"running": 1,
"max_len": 16,
"items": [ { "port": 20134, "host": "example.com", "vendor": "recaptcha",
"class": "chrome-152-win", "state": "solving",
"waiters": 1, "age_ms": 4120 } ]
}- Отдельная ручка, а не поле в /solver/stats: очередь меняется на порядки быстрее счётчиков, и панель опрашивает её своим темпом. Управления очередью здесь нет — только чтение.
/api/v1/solver/procsТребуется авторизацияЗадаёт, сколько процессов разбора работает одновременно. Применяется без перезапуска и переживает его.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
procs | int | Да | Число процессов; 0 выключает разбор. |
{ "procs": 4 }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"procs":4}' \
http://127.0.0.1:8891/api/v1/solver/procs{
"js_solver_procs": 4
}- 400 «procs must be >= 0»; 409, если запрошено больше, чем разрешает лицензия — предел виден как js_solver_max_procs в ответе GET /api/v1/license/status.
- Когда значение не задано, действует потолок лицензии: выделять процессы вручную не обязательно.
Настройки и ключи
/api/v1/settings/networkТребуется авторизацияСетевые настройки: принимают ли прокси-порты соединения из локальной сети, требуют ли они логин с паролем и по какому адресу видно список отзыва сертификатов.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/settings/network{
"allow_lan": false,
"crl_public_host": "",
"proxy_auth_enabled": true,
"proxy_auth_user": "proxy"
}- Пароль прокси в ответе не возвращается никогда — его можно только задать.
/api/v1/settings/networkТребуется авторизацияМеняет сетевые настройки. Присылать нужно только то, что меняется: поле, которого в теле нет, остаётся как было.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
allow_lan | bool | Нет | Принимать соединения на прокси-порты из локальной сети, а не только с этой машины. |
crl_public_host | string | Нет | Адрес, по которому с ДРУГОЙ машины виден список отзыва сертификатов: host или host:port, без схемы и пути. Loopback отвергается — нужен адрес, по которому эта машина видна снаружи. |
proxy_auth_enabled | bool | Нет | Требовать логин и пароль на прокси-портах. |
proxy_auth_user | string | Нет | Логин прокси. |
proxy_auth_pass | string | Нет | Пароль прокси. |
{ "allow_lan": true, "proxy_auth_enabled": true,
"proxy_auth_user": "proxy", "proxy_auth_pass": "…" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"allow_lan":true}' \
http://127.0.0.1:8891/api/v1/settings/network{
"allow_lan": true,
"crl_public_host": "",
"proxy_auth_enabled": true,
"proxy_auth_user": "proxy"
}- 400 «proxy_auth_user and proxy_auth_pass are required to enable proxy auth»: включить проверку без пары логин-пароль нельзя.
- 400 с объяснением на неверном crl_public_host; 503 «auth store not configured». Тело ограничено 8 КиБ.
/api/v1/auth/apikeyТребуется авторизацияВозвращает ключ API — тот самый, который идёт в заголовке X-API-Key.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/auth/apikey{
"api_key": "a1b2c3d4e5f6a7b8c9d0"
}- Ответ пуст, если сохранённой копии ключа нет: ключ, заданный только хешем, показать невозможно.
/api/v1/auth/apikeyТребуется авторизацияСтавит собственный ключ API вместо сгенерированного. Прежний перестаёт работать немедленно.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
api_key | string | Да | 12–128 печатных символов ASCII без пробелов: ключ едет в заголовке дословно. |
{ "api_key": "my-own-api-key-2026" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"api_key":"my-own-api-key-2026"}' \
http://127.0.0.1:8891/api/v1/auth/apikey{
"api_key": "my-own-api-key-2026"
}- 400 «api key must be 12-128 characters» или «api key must be printable ASCII without spaces». Тело ограничено 8 КиБ.
/api/v1/settings/integration-keyТребуется авторизацияСообщает, сохранён ли интеграционный ключ — он нужен, чтобы активировать лицензию от имени интегратора.
curl -H "X-API-Key: YOUR_API_KEY" \
http://127.0.0.1:8891/api/v1/settings/integration-key{
"integration_key": "",
"set": false
}/api/v1/settings/integration-keyТребуется авторизацияСохраняет или очищает интеграционный ключ. Действует без перезапуска.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
integration_key | string | Да | Ключ, до 512 символов. Пустое значение очищает сохранённый. |
{ "integration_key": "…" }curl -X PUT -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"integration_key":""}' \
http://127.0.0.1:8891/api/v1/settings/integration-key{
"ok": true
}- 400 «integration_key must be at most 512 characters»; 503 «auth store not configured».
Активация из кода
Активацию обычно проходят в дашборде, но её можно выполнить и запросом — например, разворачивая машину скриптом. Учётные данные уходят прямо в Кабинет; приложение их не хранит, а сохраняет только подписанную лицензию.
/api/v1/license/enrollТребуется авторизацияНачинает активацию учётной записью blanktrail.com.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
email | string | Да | Почта учётной записи. |
password | string | Да | Пароль учётной записи. |
{ "email": "you@example.com", "password": "…" }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"…"}' \
http://127.0.0.1:8891/api/v1/license/enroll{
"choose": false,
"activated": true,
"plan": "pro"
}- Если на учётной записи несколько лицензий, ответ приходит с choose=true, списком и разовым билетом — выбирают следующей ручкой.
- 400 «email and password are required»; 502 с состоянием внутри тела, если Кабинет недоступен; 503 «license manager not configured».
/api/v1/license/enroll/confirmТребуется авторизацияВыбирает лицензию, когда на учётной записи их несколько.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
ticket | string | Да | Разовый билет из ответа предыдущего запроса. |
license_id | int | Да | Идентификатор выбранной лицензии из того же ответа. |
{ "ticket": "…", "license_id": 42 }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"ticket":"…","license_id":42}' \
http://127.0.0.1:8891/api/v1/license/enroll/confirm{
"activated": true,
"plan": "pro"
}- 400 «ticket and license_id are required»; 502, если Кабинет недоступен.
/api/v1/auth/onboard-passwordТребуется авторизацияЗадаёт пароль дашборда на первом экране — по разовому токену bootstrap_token из ответа POST /api/v1/auth/onboard-activate, а не по прежнему паролю.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
bootstrap_token | string | Да | Разовый токен первого запуска — из ответа POST /api/v1/auth/onboard-activate. Одноразовый, живёт 15 минут. |
new_password | string | Да | Новый пароль, не короче 8 символов. |
{ "bootstrap_token": "…", "new_password": "new-password" }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"bootstrap_token":"…","new_password":"new-password"}' \
http://127.0.0.1:8891/api/v1/auth/onboard-password{
"status": "ok"
}- 401 «invalid or expired activation token»; 400 с правилом пароля, если он короче восьми символов; 501, если вход не настроен.
/api/v1/auth/onboard-activateТребуется авторизацияАктивирует лицензию на том же первом экране, не заходя в дашборд отдельно.
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
email | string | Да | Почта учётной записи. |
password | string | Да | Пароль учётной записи. |
ticket | string | Нет | Билет выбора лицензии, если их несколько. |
license_id | int | Нет | Идентификатор выбранной лицензии. |
{ "email": "you@example.com", "password": "…" }curl -X POST -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"…"}' \
http://127.0.0.1:8891/api/v1/auth/onboard-activate{
"activated": true,
"plan": "pro",
"bootstrap_token": "…"
}- Поле bootstrap_token приходит ТОЛЬКО в этом ответе и только при activated: true. Оно одноразовое и живёт 15 минут; сохраните его и передайте в POST /api/v1/auth/onboard-password. Другого источника нет — установщик этот токен не выдаёт.
- 400 «email and password are required»; 429 после череды попыток с одного адреса; 502, если Кабинет недоступен; 503 «license manager not configured».