Обзор
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-скора сессии:
Быстрый старт
Интеграция капчи за 3 шага — без SDK, без зависимостей.
Добавьте виджет в 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>Проверьте токен на сервере
# Верифицировать 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"}Готово!
При success: true — пользователь прошёл проверку. Обработайте форму.
Stitex Captcha: Клиентский виджет
Добавьте виджет на страницу с формой:
<!-- Контейнер капчи (внутри <form>) -->
<div id="stitex-captcha" data-sitekey="sc_YOUR_SITE_KEY"></div>
<!-- Скрипт виджета (перед </body>) -->
<script src="https://pulse.stitex.com/captcha/stitex-captcha.js"></script>Атрибуты контейнера
data-sitekeyобяз.sc_)data-themelight или dark. По умолчанию lightdata-callbacksolveTokenJavaScript API
// Получить текущий solveToken
const token = document.querySelector('#stitex-captcha')
?.getAttribute('data-solve-token');
// Сбросить капчу
window.stitexCaptchaReset && window.stitexCaptchaReset();
// Callback при решении
window.onStitexCaptchaSolved = function(token) {
console.log('Captcha solved:', token);
};Stitex Captcha: API эндпоинты
Все эндпоинты вызывает виджет автоматически. Документация полезна для кастомных интеграций.
/api/captcha?siteKey=YOUR_KEY30 req/minПолучить challenge для отображения пользователю.
Ответ (обычный пользователь)
{
"display": "3 + 7",
"token": "eyJ...",
"segments": ["3", "+", "7"],
"type": "math",
"difficulty": "easy"
}Ответ (доверенный пользователь — invisible)
{
"invisible": true,
"solveToken": "eyJ...",
"score": 0.98
}/api/captcha20 req/minОтправить ответ пользователя на challenge.
Тело запроса
tokenобяз.answerобяз.siteKeybehavioralОтвет
{
"valid": true,
"solveToken": "eyJ...",
"score": 0.95
}Stitex Captcha: Серверная верификация
После отправки формы проверьте solveToken на вашем сервере. Это единственный обязательный серверный вызов.
/api/captcha/verifyТело запроса (JSON)
secretKeyобяз.scs_)solveTokenобяз.stitex-captcha-token)sessionIdОтвет
{
"success": true,
"domain": "example.com",
"timestamp": "2026-03-30T12:00:00Z",
"antifraud": {
"score": 12,
"isBot": false,
"recommendation": "allow"
}
}{
"success": false,
"error": "Token expired or invalid"
}Важно: Токены одноразовые (HMAC-SHA256 с nonce). Повторная верификация того же токена вернёт ошибку. Redis обеспечивает защиту от replay-атак.
Stitex Captcha: Примеры интеграции
# Получить challenge
curl "https://pulse.stitex.com/api/captcha?siteKey=sc_YOUR_SITE_KEY"
# Ответ:
# {"display":"3 + 7","token":"eyJ...","segments":["3","+","7"],
# "type":"math","difficulty":"easy"}# Отправить ответ на 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}# Верифицировать 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"}<?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'];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']}")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 Antifraud: Виджет
Невидимый JS-виджет. Собирает поведенческие сигналы и отправляет их на сервер для ML-анализа.
<script src="https://pulse.stitex.com/captcha/stitex-antifraud.js"
data-key="YOUR_API_KEY" async></script>Атрибуты
data-keyобяз.Что собирается
Collect API (внутренний)
/api/antifraud/collectВызывается JS-виджетом автоматически. Документация для справки.
keyобяз.sidобяз.urlrefuadurclicksmovesscrollPctscrollEventsavgMoveMssignalsmouseTrajectorykeystrokeDynamics{"ok": true, "score": 15}Stitex Antifraud: Серверная проверка
Проверьте fraud score конкретной сессии на вашем сервере.
/api/antifraud/checkТело запроса (JSON)
apiKeyобяз.sessionIdобяз.stitex_af_sid)Полная схема ответа
{
"found": true,
"score": 78,
"isBot": true,
"reasons": ["webdriver", "headless_chrome", "sw_renderer"],
"recommendation": "block",
"requireCaptcha": false,
"sessionId": "sess_abc123..."
}Значения recommendation
| Recommendation | Score | Описание |
|---|---|---|
allow | < 25 | Нормальный пользователь — пропускать |
monitor | 25 – 50 | Подозрительная активность — логировать |
captcha | 50 – 70 | Показать Stitex Captcha для проверки |
block | ≥ 70 | Вероятный бот — заблокировать доступ |
Stitex Antifraud: Примеры интеграции
# Проверить 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..."}<?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
}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']}")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).
<!-- 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
// 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) начинается с заданного количества нулей.
/api/captcha/powПолучить PoW challenge.
Ответ
{
"challenge": "a1b2c3d4e5f6...",
"difficulty": 4,
"prefix": "0000",
"token": "eyJ...",
"hint": "Find nonce where SHA-256(challenge+nonce) starts with 0000"
}/api/captcha/powОтправить решение PoW.
Тело запроса
challengeобяз.nonceобяз.tokenобяз.Ответ
{
"valid": true,
"powToken": "eyJ..."
}curl-примеры
# Получить 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"}# Отправить решение 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
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/captcha | GET | 30 запросов | 1 минута |
/api/captcha | POST | 20 запросов | 1 минута |
/api/captcha/verify | POST | — | без лимита |
/api/captcha/pow | GET | 30 запросов | 1 минута |
/api/captcha/pow | POST | 20 запросов | 1 минута |
/api/antifraud/collect | POST | — | без лимита |
/api/antifraud/check | POST | — | без лимита |
Заголовки ответа при 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) |
429 | Rate limit превышен | Подождите Retry-After секунд |
500 | Внутренняя ошибка | Повторите позже или напишите sti@stitex.com |
Формат ошибки
{
"error": "Token expired or invalid",
"code": "INVALID_TOKEN"
}FAQ
Сервис бесплатный?+
Какие типы captcha поддерживаются?+
Что такое invisible captcha?+
Зачем нужен Proof-of-Work?+
Можно ли использовать только Antifraud без Captcha?+
Как токены защищены от replay-атак?+
Какие данные antifraud отправляет на сервер?+
Где хранятся данные?+
Нужна помощь с интеграцией?
Напишите нам — поможем настроить за 10 минут.