Документация Stitex Captcha & Antifraud API

Быстрый старт, примеры на PHP, Python, Node.js и curl, описание эндпоинтов, Proof-of-Work и лимитов.

Обзор

Stitex предоставляет два бесплатных сервиса защиты от ботов:

Stitex Captcha

Активная проверка — пользователь решает задание (математика, слова, последовательности). Включает Proof-of-Work и поведенческий анализ. Адаптивная сложность на основе ML-модели антифрода.

Stitex Antifraud

Невидимый анализ — JS-виджет собирает 30+ сигналов (fingerprint, поведение, окружение) и вычисляет fraud score 0–100 в реальном времени.

Оба сервиса можно использовать отдельно или вместе. При совместном использовании antifraud score автоматически влияет на сложность captcha.

Базовый URL: https://pulse.stitex.com

Captcha виджет: https://pulse.stitex.com/captcha/stitex-captcha.js

Antifraud виджет: https://pulse.stitex.com/captcha/stitex-antifraud.js

Ключи: sc_... (siteKey), scs_... (secretKey)

Получить ключи: pulse.stitex.com/register

Адаптивная сложность captcha

Сложность автоматически выбирается на основе antifraud ML-скора сессии:

Invisible
score < 0.3
Easy
score < 0.6
Medium
score < 0.8
Hard
score ≥ 0.8

Быстрый старт

Интеграция капчи за 3 шага — без SDK, без зависимостей.

1

Добавьте виджет в HTML

<form method="POST" action="/submit">
  <!-- Ваши поля формы -->
  <input type="email" name="email" required />

  <!-- Captcha виджет -->
  <div id="stitex-captcha" data-sitekey="sc_YOUR_SITE_KEY"></div>

  <button type="submit">Отправить</button>
</form>

<script src="https://pulse.stitex.com/captcha/stitex-captcha.js"></script>
2

Проверьте токен на сервере

curlbash
# Верифицировать solveToken на сервере
curl -X POST https://pulse.stitex.com/api/captcha/verify \
  -H "Content-Type: application/json" \
  -d '{
    "secretKey": "scs_YOUR_SECRET_KEY",
    "solveToken": "eyJhbGciOiJIUzI1NiJ9..."
  }'

# Ответ:
# {"success":true,"domain":"example.com","timestamp":"2026-03-30T12:00:00Z"}
3

Готово!

При success: true — пользователь прошёл проверку. Обработайте форму.

Stitex Captcha: Клиентский виджет

Добавьте контейнер внутрь формы и подключите скрипт один раз на страницу:

HTML — вставьте в формуhtml
<form method="POST" action="/send">
  <input name="name">
  <!-- Контейнер капчи: id="stitex-captcha", класс .stitex-captcha или атрибут data-stitex-captcha -->
  <div class="stitex-captcha" data-sitekey="sc_YOUR_SITE_KEY" data-theme="light"></div>
  <button type="submit">Отправить</button>
</form>

<!-- Скрипт виджета — один на страницу (перед </body>) -->
<script src="https://pulse.stitex.com/captcha/stitex-captcha.js" async></script>

Несколько виджетов на странице. Каждый контейнер с классом .stitex-captcha или атрибутом data-stitex-captcha получает свой виджет. Контейнеры, добавленные позже (модальные окна, SPA), рисуются сами; очищенный контейнер рисуется заново; повторная вставка скрипта ничего не ломает. После проверки в форму контейнера добавляется скрытое поле stitex-captcha-token, токен кладётся в window.stitexCaptchaTokenи передаётся в data-callback. Токен живёт 10 минут и одноразовый.

Атрибуты контейнера

data-sitekeyобяз.
string
Site Key вашего сайта из кабинета
data-theme
string
Тема виджета: light или dark (по умолчанию dark)
data-callback
string
Имя глобальной функции (можно через точку: app.onCaptcha), вызывается с токеном
data-expired-callback
string
Функция, вызываемая, когда токен истёк (виджет сам запросит новую проверку)
data-stx-mode
string
early — ранний токен без привязки к форме (для форм, которые отправляются скриптом). См. «Captcha v3»

JavaScript API

Программная работа с виджетомjavascript
// Нарисовать виджет в своём контейнере (вернёт id виджета)
const id = StitexCaptcha.render(document.querySelector('#my-captcha'), {
  sitekey: 'sc_YOUR_SITE_KEY',
  theme: 'light',
  callback: function (token) { console.log('токен', token) },
  expiredCallback: function () { console.log('токен истёк') },
})

StitexCaptcha.getResponse(id)   // текущий токен ('' — ещё не готов); без id — последний виджет
StitexCaptcha.reset(id)         // сбросить и показать проверку заново
StitexCaptcha.execute(id)       // v3: получить ранний токен без привязки к форме (AJAX)
window.stitexCaptchaToken       // последний полученный токен

Stitex Captcha: API эндпоинты

Все эндпоинты вызывает виджет автоматически. Документация полезна для кастомных интеграций.

GET/api/captcha?siteKey=YOUR_KEY30 req/min

Получить challenge для отображения пользователю.

Ответ (обычный пользователь)

{
  "image": "data:image/png;base64,iVBORw0...",
  "width": 260,
  "height": 68,
  "token": "...",
  "type": "math",
  "difficulty": "easy"
}

Ответ (доверенный пользователь — invisible)

{
  "invisible": true,
  "solveToken": "eyJ...",
  "score": 0.98
}
POST/api/captcha20 req/min

Отправить ответ пользователя на challenge.

Тело запроса

tokenобяз.
string
Токен из GET-ответа
answerобяз.
string
Ответ пользователя
siteKey
string
Site Key (опционально, для валидации)
behavioral
object
Поведенческие данные (мышь, клавиши, время)

Ответ

{
  "valid": true,
  "solveToken": "eyJ...",
  "score": 0.95
}

Stitex Captcha: Серверная верификация

После отправки формы проверьте solveToken на вашем сервере. Это единственный обязательный серверный вызов.

POST/api/captcha/verify

Тело запроса (JSON)

secretKeyобяз.
string
Secret Key вашего сайта (начинается с scs_)
solveTokenобяз.
string
Токен из формы (скрытое поле stitex-captcha-token)
sessionId
string
ID сессии antifraud (для связки данных)
formHash
string
Только v3: хэш содержимого формы fh1:… (см. «Captcha v3»). Передаётся вместе с formShape
formShape
string
Только v3: хэш состава полей формы fs1:…

Ответ

Успешная верификацияjson
{
  "success": true,
  "domain": "example.com",
  "timestamp": "2026-03-30T12:00:00Z",
  "antifraud": {
    "score": 12,
    "isBot": false,
    "recommendation": "allow"
  }
}
Ошибка верификацииjson
{
  "success": false,
  "error": "Token expired or invalid"
}

Важно: Токены одноразовые (HMAC-SHA256 с nonce). Повторная верификация того же токена вернёт ошибку. Redis обеспечивает защиту от replay-атак.

Stitex Captcha: Примеры интеграции

curl — получить challengebash
# Получить challenge
curl -H "Origin: https://your-site.ru" "https://pulse.stitex.com/api/captcha?siteKey=sc_YOUR_SITE_KEY&theme=light"

# Ответ: пример только картинкой, текста задачи в ответе нет.
# Origin обязан совпадать с доменом сайта (или его поддоменом).
# {"image":"data:image/png;base64,...","width":260,"height":68,
#  "token":"...","type":"math","difficulty":"easy"}
curl — отправить ответbash
# Отправить ответ на challenge
curl -X POST https://pulse.stitex.com/api/captcha \
  -H "Content-Type: application/json" \
  -d '{
    "token": "eyJ...",
    "answer": "10",
    "siteKey": "sc_YOUR_SITE_KEY"
  }'

# Ответ:
# {"valid":true,"solveToken":"eyJ...","score":0.95}
curl — серверная верификацияbash
# Верифицировать solveToken на сервере
curl -X POST https://pulse.stitex.com/api/captcha/verify \
  -H "Content-Type: application/json" \
  -d '{
    "secretKey": "scs_YOUR_SECRET_KEY",
    "solveToken": "eyJhbGciOiJIUzI1NiJ9..."
  }'

# Ответ:
# {"success":true,"domain":"example.com","timestamp":"2026-03-30T12:00:00Z"}
PHPphp
<?php
$token = $_POST['stitex-captcha-token'] ?? '';

$response = file_get_contents(
  'https://pulse.stitex.com/api/captcha/verify',
  false,
  stream_context_create([
    'http' => [
      'method'  => 'POST',
      'header'  => 'Content-Type: application/json',
      'content' => json_encode([
        'secretKey'  => 'scs_YOUR_SECRET_KEY',
        'solveToken' => $token,
      ]),
    ],
  ])
);

$data = json_decode($response, true);

if (!$data || !$data['success']) {
  die('Капча не пройдена');
}

// Капча пройдена
echo 'Score: ' . $data['score'];
Python (requests)python
import requests

token = request.form.get('stitex-captcha-token', '')

resp = requests.post(
    'https://pulse.stitex.com/api/captcha/verify',
    json={
        'secretKey': 'scs_YOUR_SECRET_KEY',
        'solveToken': token,
    }
)
data = resp.json()

if not data.get('success'):
    return 'Капча не пройдена', 403

print(f"Score: {data['score']}")
Node.js (fetch)javascript
const resp = await fetch(
  'https://pulse.stitex.com/api/captcha/verify',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      secretKey: process.env.STITEX_SECRET_KEY,
      solveToken: req.body['stitex-captcha-token'],
    }),
  }
);
const data = await resp.json();

if (!data.success) {
  return res.status(403).json({ error: 'Captcha failed' });
}

console.log('Score:', data.score);

Stitex Captcha v3: невидимая проверка и привязка к форме

v3 — многослойная проверка, которую обычный посетитель не замечает. Виджет оценивает сеть (база дата-центров и общая репутация адресов по всем сайтам), подлинность браузера (проба, новая на каждый запрос), поведение (ML по траектории мыши и ритму ввода, без кодов клавиш) и скрытые ловушки для ботов и ИИ-агентов (экранные дикторы их не видят и на них не попадают), а затем назначает цену — короткое вычисление в браузере. Картинка показывается только при высоком риске, и рядом с ней всегда есть кнопка «Не вижу картинку»: проверка без зрения проходит более долгим вычислением.

Код на странице не меняется: тот же скрипт, тот же контейнер, то же поле stitex-captcha-token, тот же /api/captcha/verify. Для привязки токена к форме сервер передаёт два хэша формы (ниже).

Режимы

Выключен — работает прежняя проверка-пример (v2).

Теневой — посетитель проходит прежнюю проверку, а v3 в фоне считает своё решение и пишет статистику: так до включения видно, кого v3 задел бы и кого пропустил бы.

Боевой — включается для сайта после теневого замера. Виджет показывает «Защищено — проверка при отправке»; при отправке формы проверка идёт сама, и форма уходит с токеном v3 (stx3.…). Если сервис проверки недоступен, виджет переходит на проверку v2 — форма не блокируется.

Режим сайта включает Stitex; статистика теневого режима — в кабинете, раздел «Капча».

Привязка токена к форме

Токен v3 выдаётся под содержимое конкретной формы: токен, решённый на другой форме или фермой решателей, не подойдёт. Ваш сервер считает два хэша принятой формы и передаёт их в /api/captcha/verify как formHash и formShape — только парой. Содержимое формы к нам не уходит, только хэши. Готовые функции — ниже.

  1. Пары (имя, значение) — ровно то, что пришло в POST, в исходном виде (файлы не включаются). Поле stitex-captcha-token и все поля с префиксом stx- исключаются.
  2. Имя и значение приводятся к Unicode NFC, переводы строк \r\n и \r — к \n. Пробелы не обрезаются.
  3. Пары сортируются побайтово (UTF-8): по имени, затем по значению.
  4. formHash = "fh1:" + sha256hex( для каждой пары len(имя):имя,len(значение):значение; ) — длина в байтах UTF-8.
  5. formShape = "fs1:" + sha256hex( для каждого уникального имени len(имя):имя; ).

formCheck: "match" — форма та же. "mismatch" — состав полей тот же, а значения изменились после проверки (например, маска телефона): токен принимается с этой пометкой, решение — за вами. Другой состав полей — всегда отказ shape_mismatch.

Формы, которые отправляются скриптом (AJAX): атрибут data-stx-mode="early" или StitexCaptcha.execute() дают ранний токен без привязки (formCheck: "unbound"), проверяйте его без хэшей. Если хэши переданы, а токен без привязки, — отказ form_unbound.

После проверки поля в форму не дописывайте. Хэш формы считается в момент отправки — если ваш скрипт потом добавляет поле в FormData (data.append("result", …)), состав полей на сервере не совпадёт и проверка ответит shape_mismatch. Три решения: положите значение в скрытое поле формы до отправки; или включите ранний токен; или передайте SDK список дописываемых полей — ignore (Python, Node) / последний аргумент stitex_verify (PHP): эти поля не войдут в хэши.

Сервер, который шлёт в проверку только secretKey и solveToken (пример v2), продолжает работать: ответ придёт с formCheck: "missing". Строгая привязка (без хэшей — отказ form_hash_missing, другое содержимое — form_mismatch) включается для сайта по запросу в поддержку — сначала переведите сервер на SDK.

Ответ /api/captcha/verify для токена v3

Успех (HTTP 200)json
{
  "success": true,
  "outcome": "pass",
  "version": 3,
  "domain": "example.com",
  "timestamp": 1760000000000,  // время выдачи, мс
  "score": 0.82,        // 0..1, 1 — человек (та же шкала, что в v2)
  "risk": 18,           // 0..100
  "reasons": ["net.datacenter"],
  "action": "pow",      // каким путём выдан: pass | pow | image
  "powLevel": 1,
  "formCheck": "match", // match | mismatch | missing | unbound
  "antifraud": null
}
Отказ (HTTP 400) и недоступность (HTTP 503)json
{ "success": false, "outcome": "fail",  "version": 3, "error": "replay" }
{ "success": false, "outcome": "error", "version": 3, "error": "unavailable" }

Три исхода, не два. outcome: "error" — хранилище Stitex не ответило: это не «да» и не «нет». Повторите запрос или примите заявку с пометкой — решаете вы. Токен при этом не погашен.

Коды отказа: format, version, kid, signature — токен испорчен или подделан; expired — старше 10 минут; site — токен другого сайта; replay — уже погашен (токен одноразовый); form_unbound, form_hash_missing, shape_mismatch, form_mismatch — привязка к форме. Для v3 тело запроса проверяется строго: лишнее поле — unknown_field, неверный тип — bad_type, нет обязательного — missing, слишком длинное значение — too_large.

SDK для сервера: PHP, Python, Node.js

Готовые файлы без зависимостей: считают хэши формы и вызывают проверку; один вызов подходит и для токенов v2 (хэши тогда не передаются). Скачать: stitex_captcha.php, stitex_captcha.py, stitex-captcha.js. В каждом файле есть самопроверка на общих векторах (--vectors).

PHPphp
<?php
// Скачайте https://pulse.stitex.com/api/captcha/v3/sdk/stitex_captcha.php
require __DIR__ . '/stitex_captcha.php';

// Пары формы с исходными именами (повторы и поля вида a[] сохраняются)
$pairs = stitex_pairs_from_request();
$res = stitex_verify(getenv('STITEX_SECRET_KEY'), $pairs);

if ($res['outcome'] === 'pass') {
  // принять заявку; $res['formCheck'] === 'mismatch' — значения правились после проверки
  // (скрипт дописывает поле в FormData после проверки: stitex_verify($key, $pairs, null, true, ['result']))
} elseif ($res['outcome'] === 'error') {
  // Stitex не ответил: повторить или принять с пометкой — решаете вы
} else {
  http_response_code(403);
  exit('Проверка не пройдена');
}
Pythonpython
# Скачайте https://pulse.stitex.com/api/captcha/v3/sdk/stitex_captcha.py
import os
from stitex_captcha import verify, pairs_from_urlencoded

# Flask: СЫРОЕ тело формы (сохраняет повторы и имена вида a[])
pairs = pairs_from_urlencoded(request.get_data(as_text=True))
res = verify(os.environ["STITEX_SECRET_KEY"], pairs)

if res["outcome"] == "pass":
    ...                          # принять заявку
elif res["outcome"] == "error":
    ...                          # Stitex не ответил: повторить или принять с пометкой
else:
    return "Проверка не пройдена", 403

# Ранний токен (AJAX, StitexCaptcha.execute): verify(key, [], token=t, bind=False)
# Скрипт дописывает поле в FormData после проверки: verify(key, pairs, ignore=("result",))
Node.jsjavascript
// Скачайте https://pulse.stitex.com/api/captcha/v3/sdk/stitex-captcha.js
const { verify, pairsFromUrlencoded } = require('./stitex-captcha');

// Express: нужно СЫРОЕ тело формы
app.post('/send', express.text({ type: 'application/x-www-form-urlencoded' }), async (req, res) => {
  const r = await verify(process.env.STITEX_SECRET_KEY, pairsFromUrlencoded(req.body));
  if (r.outcome === 'pass') return res.send('ok');
  if (r.outcome === 'error') return res.status(503).send('Повторите позже');   // или принять с пометкой
  return res.status(403).send('Проверка не пройдена');
});

// Ранний токен (AJAX): await verify(key, [], token, { bind: false })
// Скрипт дописывает поле в FormData после проверки: await verify(key, pairs, undefined, { ignore: ['result'] })

Stitex Antifraud: Виджет

Невидимый JS-виджет. Собирает поведенческие сигналы и отправляет их на сервер для ML-анализа.

HTML — вставьте перед </body>html
<script src="https://pulse.stitex.com/captcha/stitex-antifraud.js"
        data-key="YOUR_API_KEY" async></script>

Атрибуты

data-keyобяз.
string
API Key вашего сайта из Dashboard (раздел Antifraud)

Что собирается

User-Agent
Screen / Window size
Timezone
Canvas fingerprint
WebGL vendor/renderer
AudioContext FP
Installed plugins
WebDriver API
Chrome headless
Mouse trajectory
Keystroke dynamics
Scroll behavior
Focus/blur events
Touch events
Connection type

Collect API (внутренний)

POST/api/antifraud/collect

Вызывается JS-виджетом автоматически. Документация для справки.

keyобяз.
string
API Key сайта
sidобяз.
string
Session ID
url
string
Текущий URL страницы
ref
string
Referrer
ua
string
User-Agent
dur
number
Время на странице (мс)
clicks
number
Количество кликов
moves
number
Количество движений мыши
scrollPct
number
Процент прокрутки
scrollEvents
number
Количество scroll-событий
avgMoveMs
number
Среднее время между движениями мыши
signals
object
Fingerprint-данные (canvas, webgl, audio и т.д.)
mouseTrajectory
array
Траектория мыши (массив координат)
keystrokeDynamics
array
Динамика нажатий клавиш
Ответjson
{"ok": true, "score": 15}

Stitex Antifraud: Серверная проверка

Проверьте fraud score конкретной сессии на вашем сервере.

POST/api/antifraud/check

Тело запроса (JSON)

apiKeyобяз.
string
API Key вашего сайта
sessionIdобяз.
string
ID сессии (cookie stitex_af_sid)

Полная схема ответа

Ответjson
{
  "found": true,
  "score": 78,
  "isBot": true,
  "reasons": ["webdriver", "headless_chrome", "sw_renderer"],
  "recommendation": "block",
  "requireCaptcha": false,
  "sessionId": "sess_abc123..."
}

Значения recommendation

RecommendationScoreОписание
allow< 25Нормальный пользователь — пропускать
monitor25 – 50Подозрительная активность — логировать
captcha50 – 70Показать Stitex Captcha для проверки
block≥ 70Вероятный бот — заблокировать доступ

Stitex Antifraud: Примеры интеграции

curlbash
# Проверить fraud-скор сессии
curl -X POST https://pulse.stitex.com/api/antifraud/check \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "YOUR_API_KEY",
    "sessionId": "sess_abc123..."
  }'

# Ответ:
# {"found":true,"score":78,"isBot":true,
#  "recommendation":"block",
#  "reasons":["webdriver","headless_chrome"],
#  "requireCaptcha":false,"sessionId":"sess_abc123..."}
PHPphp
<?php
$sessionId = $_COOKIE['stitex_af_sid'] ?? '';
if (!$sessionId) {
  die('No antifraud session');
}

$response = file_get_contents(
  'https://pulse.stitex.com/api/antifraud/check',
  false,
  stream_context_create([
    'http' => [
      'method'  => 'POST',
      'header'  => 'Content-Type: application/json',
      'content' => json_encode([
        'apiKey'    => 'YOUR_API_KEY',
        'sessionId' => $sessionId,
      ]),
    ],
  ])
);

$data = json_decode($response, true);

if ($data && $data['recommendation'] === 'block') {
  http_response_code(403);
  die('Доступ запрещён');
}

if ($data && $data['recommendation'] === 'captcha') {
  // Показать Stitex Captcha
}
Python (requests)python
import requests

session_id = request.cookies.get('stitex_af_sid', '')

resp = requests.post(
    'https://pulse.stitex.com/api/antifraud/check',
    json={
        'apiKey': 'YOUR_API_KEY',
        'sessionId': session_id,
    }
)
data = resp.json()

if data.get('recommendation') == 'block':
    abort(403)
elif data.get('recommendation') == 'captcha':
    # Показать Stitex Captcha
    pass

print(f"Score: {data['score']}, Bot: {data['isBot']}")
Node.js (Express middleware)javascript
async function antifraudMiddleware(req, res, next) {
  const sessionId = req.cookies['stitex_af_sid'];
  if (!sessionId) return next();

  const resp = await fetch(
    'https://pulse.stitex.com/api/antifraud/check',
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        apiKey: process.env.STITEX_AF_KEY,
        sessionId,
      }),
    }
  );
  const data = await resp.json();

  if (data.recommendation === 'block') {
    return res.status(403).json({ error: 'Blocked' });
  }
  if (data.recommendation === 'captcha') {
    return res.status(418).json({ requireCaptcha: true });
  }

  req.fraudScore = data.score;
  req.isBot = data.isBot;
  next();
}

Совместное использование Captcha + Antifraud

При совместном использовании Antifraud автоматически влияет на сложность Captcha. Ботам показывается сложная капча, обычным пользователям — невидимая (invisible mode).

HTML — полная интеграцияhtml
<!-- Antifraud (невидимый, в <head> или перед </body>) -->
<script src="https://pulse.stitex.com/captcha/stitex-antifraud.js"
        data-key="YOUR_API_KEY" async></script>

<!-- Captcha виджет (внутри формы) -->
<form method="POST" action="/submit">
  <input type="text" name="email" placeholder="Email" required />
  <textarea name="message" placeholder="Сообщение" required></textarea>

  <div id="stitex-captcha" data-sitekey="sc_YOUR_SITE_KEY"></div>

  <button type="submit">Отправить</button>
</form>

<script src="https://pulse.stitex.com/captcha/stitex-captcha.js"></script>
Серверная проверка (PHP)php
<?php
// 1. Проверяем Captcha
$token = $_POST['stitex-captcha-token'] ?? '';
$captcha = json_decode(file_get_contents(
  'https://pulse.stitex.com/api/captcha/verify',
  false,
  stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/json',
    'content' => json_encode([
      'secretKey'  => 'scs_CAPTCHA_SECRET',
      'solveToken' => $token,
    ]),
  ]])
), true);

if (!$captcha || !$captcha['success']) {
  die('Капча не пройдена');
}

// 2. Проверяем Antifraud (опционально)
$sid = $_COOKIE['stitex_af_sid'] ?? '';
if ($sid) {
  $af = json_decode(file_get_contents(
    'https://pulse.stitex.com/api/antifraud/check',
    false,
    stream_context_create(['http' => [
      'method'  => 'POST',
      'header'  => 'Content-Type: application/json',
      'content' => json_encode([
        'apiKey'    => 'ANTIFRAUD_API_KEY',
        'sessionId' => $sid,
      ]),
    ]])
  ), true);

  if ($af && $af['recommendation'] === 'block') {
    die('Заблокировано антифродом');
  }
}

// Всё ОК — обрабатываем форму

Proof-of-Work (PoW)

PoW используется как дополнительная защита при повторных неудачных попытках решения капчи. Клиент должен найти nonce, при котором SHA-256(challenge + nonce) начинается с заданного количества нулей.

GET/api/captcha/pow

Получить PoW challenge.

Ответ

{
  "challenge": "a1b2c3d4e5f6...",
  "difficulty": 4,
  "prefix": "0000",
  "token": "eyJ...",
  "hint": "Find nonce where SHA-256(challenge+nonce) starts with 0000"
}
POST/api/captcha/pow

Отправить решение PoW.

Тело запроса

challengeобяз.
string
Challenge из GET-ответа
nonceобяз.
string
Найденный nonce
tokenобяз.
string
Токен из GET-ответа

Ответ

{
  "valid": true,
  "powToken": "eyJ..."
}

curl-примеры

curl — получить PoW challengebash
# Получить PoW challenge
curl "https://pulse.stitex.com/api/captcha/pow"

# Ответ:
# {"challenge":"a1b2c3...","difficulty":4,"prefix":"0000",
#  "token":"eyJ...","hint":"Find nonce where SHA-256(challenge+nonce) starts with 0000"}
curl — отправить решениеbash
# Отправить решение PoW
curl -X POST https://pulse.stitex.com/api/captcha/pow \
  -H "Content-Type: application/json" \
  -d '{
    "challenge": "a1b2c3...",
    "nonce": "84721",
    "token": "eyJ..."
  }'

# Ответ:
# {"valid":true,"powToken":"eyJ..."}

JavaScript — решатель PoW

Пример решения PoW в браузереjavascript
async function solveProofOfWork() {
  // 1. Получаем challenge
  const res = await fetch('https://pulse.stitex.com/api/captcha/pow');
  const { challenge, difficulty, token } = await res.json();

  // 2. Brute-force nonce (SHA-256)
  const prefix = '0'.repeat(difficulty);
  let nonce = 0;

  while (true) {
    const input = challenge + nonce;
    const hash = await crypto.subtle.digest(
      'SHA-256',
      new TextEncoder().encode(input)
    );
    const hex = Array.from(new Uint8Array(hash))
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');

    if (hex.startsWith(prefix)) break;
    nonce++;
  }

  // 3. Отправляем решение
  const verify = await fetch('https://pulse.stitex.com/api/captcha/pow', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ challenge, nonce: String(nonce), token }),
  });
  const result = await verify.json();
  return result.powToken; // Используйте вместо solveToken
}

Примечание: Difficulty 4 (4 нуля) означает ~65 000 итераций в среднем. На современном устройстве это занимает 1–3 секунды. Виджет решает PoW автоматически, когда это необходимо.

Rate Limits

Все лимиты применяются per IP через Redis. При превышении возвращается 429 Too Many Requests.

ЭндпоинтМетодЛимитОкно
/api/captchaGET30 запросов1 минута
/api/captchaPOST20 запросов1 минута
/api/captcha/verifyPOST—без лимита
/api/captcha/powGET30 запросов1 минута
/api/captcha/powPOST20 запросов1 минута
/api/antifraud/collectPOST—без лимита
/api/antifraud/checkPOST—без лимита

Заголовки ответа при rate limit

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711800090

Коды ошибок

HTTPОписаниеЧто делать
200УспехОбработайте ответ
400Неверные параметрыПроверьте тело запроса и обязательные поля
401Неверный ключПроверьте secretKey (scs_) или apiKey
403Домен не разрешёнДобавьте домен в Dashboard
404Сессия / токен не найденыПроверьте sessionId / solveToken
409Токен уже использованКаждый solveToken одноразовый (replay protection)
429Rate limit превышенПодождите Retry-After секунд
500Внутренняя ошибкаПовторите позже или напишите sti@stitex.com

Формат ошибки

{
  "error": "Token expired or invalid",
  "code": "INVALID_TOKEN"
}

FAQ

Сервис бесплатный?+
Да, полностью бесплатный. Нет ограничений по количеству запросов (кроме rate limits для защиты от злоупотреблений).
Какие типы captcha поддерживаются?+
Математические задачи, текстовые последовательности, визуальные паттерны. Тип и сложность выбираются автоматически на основе fraud score.
Что такое invisible captcha?+
Если antifraud ML-модель оценивает пользователя как доверенного (score < 0.3), капча не показывается — solveToken выдаётся автоматически.
Зачем нужен Proof-of-Work?+
PoW активируется при повторных неудачных попытках решения. Это замедляет brute-force атаки, заставляя клиент тратить вычислительные ресурсы.
Можно ли использовать только Antifraud без Captcha?+
Да, оба сервиса полностью независимы. Подключите только antifraud-виджет и проверяйте score на сервере.
Как токены защищены от replay-атак?+
Каждый токен подписан HMAC-SHA256 с уникальным nonce. После использования nonce сохраняется в Redis и повторное использование блокируется.
Какие данные antifraud отправляет на сервер?+
Только анонимные сигналы: fingerprint (canvas, WebGL, audio), поведение (мышь, клавиатура, скролл), метаданные (UA, экран, timezone). Персональные данные не собираются.
Где хранятся данные?+
Все данные обрабатываются в реальном времени и хранятся во временном Redis-хранилище. Сессии удаляются автоматически.

Нужна помощь с интеграцией?

Напишите нам — поможем настроить за 10 минут.