Документация
Один скрипт в браузере, один вызов API с вашего сервера. Всё, что нужно для запуска, на одной странице.
Быстрый старт
Вам понадобятся публичный ключ (pk_...) для браузера и секретный ключ (sk_...) для сервера. Оба находятся в панели, в разделе «Сайты и ключи».
1. Добавьте скрипт
Разместите этот тег на страницах, где пользователи регистрируются, входят, кликают по офферам или запрашивают вывод. Скрипт инициализируется сам, используя data-key
<script src="https://cashoutguard.com/v1/agent.js" data-key="pk_live_YOUR_PUBLIC_KEY" async></script>
2. Соберите request_id и отправьте его на сервер
Вызовите collect() в нужный момент, передав название события. Результат: { request_id, visitor_id }
const { request_id } = await CashoutGuard.collect({ event: 'cashout' });
collect() никогда не завершается reject. При сбое (блокировка, офлайн, таймаут) он возвращает request_id: null и строку с ошибкой. Всё равно отправляйте форму, а решение пусть принимает сервер.
3. Запросите решение с сервера
Отправьте событие POST-запросом на /v1/evaluate с секретным ключом в заголовке Authorization: Bearer sk_live_...
Никогда не размещайте секретный ключ в коде браузера или мобильного приложения. Вызывать /v1/evaluate должен только ваш сервер.
Запрос evaluate
POST https://cashoutguard.com/v1/evaluate оценивает одно событие для одного аккаунта, связывает его с устройством, сетью и адресом выплаты и возвращает решение. Отправляйте JSON.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
event | string | Обязательно | signup, login, offer_click, conversion, cashout или custom. |
account_id | string | Обязательно | ID пользователя у вас, до 191 символа. Используйте непрозрачный ID, а не email. |
request_id | string | Необязательно | Из collect() в браузере. Действует 1 час. Без него у нас есть только данные о сети. |
ip | string | Необязательно | IP пользователя. Нужен, только если у вас нет request_id, например для postback. |
email | string | Необязательно | Хранится в виде хеша. Выявляет одноразовые домены и email, общие для нескольких аккаунтов. |
payout_address | string | Необязательно | Email PayPal, кошелёк или счёт, куда уходят деньги. Хранится в виде хеша. |
payout_method | string | Необязательно | Произвольный текст, например paypal, usdt или giftcard. |
amount | number | Необязательно | Сумма вывода или конверсии. |
currency | string | Необязательно | Код валюты, например USD. Для выводов по умолчанию USD. |
offer_id | string | Необязательно | ID оффера для offer_click и conversion. Используется для расчёта скорости выполнения. |
offer_country | string | Необязательно | Страна, за которую платит оффер (код ISO, например US), в конверсиях. Пользователь в другой стране помечается. |
geo_country | string | Необязательно | Ваша собственная геолокация IP пользователя (код ISO). Если она расходится с нашей, IP, скорее всего, выход прокси. |
transaction_id | string | Необязательно | ID транзакции конверсии в сети. Используется для поиска дублей. |
offer_name | string | Необязательно | Название оффера так, как его видят пользователи. Показывается в панели вместо сырого id. |
click_id | string | Необязательно | Ваш id клика. Передавайте одно и то же значение в offer_click и в конверсии, чтобы связать их. |
source | string | Необязательно | Источник события, например название оффервола или сети. |
meta | object | Необязательно | До 20 дополнительных текстовых полей, сохраняемых с событием (например, sub id). |
Ответ
Ответ всегда имеет одинаковую структуру. Неприменимые поля равны null.
scoreпринимает значение от 0 до 100. Причины комбинируются так, что несколько средних сигналов складываются, не превышая 100.reasonsперечисляет все сработавшие сигналы, начиная с самого сильного, с их весом, подробностями и понятной английской подписью, которую можно показать как есть.device.confidenceпоказывает, насколько уникально выглядит устройство. Отпечатки можно подделать, поэтому мы никогда не заявляем стопроцентную уверенность.linked_accountsперечисляет другие аккаунты с тем же устройством, адресом выплаты или email.cashoutзаполняется только для событий cashout и показывает статус в вашей очереди проверки.
Решения и action
Ориентируйтесь на поле action, а не на decision. Пороги ниже заданы по умолчанию, их можно изменить для каждого сайта в панели.
| Решение | Оценка (по умолчанию) | action в shadow | action в enforce | Что делать |
|---|---|---|---|---|
allow | 0-49 | allow | allow | Продолжайте как обычно. |
review | 50-79 | allow | review | Задержите вывод или запросите дополнительную проверку. Событие появится в вашей очереди проверки. |
block | 80-100 | allow | block | Остановите действие. Заморозьте баланс или откажите в регистрации. |
В теневом режиме action всегда равно allow, поэтому тот же код можно безопасно выкатить до включения enforce. При переключении на enforce в вашем коде ничего не меняется.
Типы событий
Отправляйте те события, которые у вас есть. Важнее всего signup и cashout; conversion нужны для проверки скорости выполнения офферов.
| Событие | Когда отправлять | Что ловит |
|---|---|---|
signup | Аккаунт создан | Мультиаккаунты, боты, регистрации через VPN, одноразовые email |
login | Успешный вход | Совместное использование аккаунта, смена страны, новые устройства |
offer_click | Пользователь открывает оффер | Запускает отсчёт для проверки скорости выполнения |
conversion | Postback от offerwall или сети | Слишком быстро выполненные офферы, дубли транзакций, всплески конверсий |
cashout | Пользователь запрашивает вывод | Общие адреса выплат, частые выводы, выводы с новых аккаунтов |
custom | Всё остальное, что вы хотите оценить | Сигналы устройства и сети |
Офферволы и постбэки
Конверсии офферволов приходят постбэком, сервер к серверу, без браузера. CashoutGuard связывает каждую конверсию с кликом, с которого она началась, и оценивает её по устройству и сети этого клика.
1. Когда пользователь открывает оффер
Соберите данные в браузере и отправьте offer_click с оффером и вашим id клика.
2. Когда приходит постбэк
Отправьте conversion из обработчика постбэков с тем же account_id и click_id. request_id не нужен. Передайте IP пользователя, если оффервол его сообщает.
Конверсии проверяются на: слишком быстрое выполнение для оффера, конверсию без клика, клик и конверсию из разных стран, заработок намного выше обычного дня аккаунта и повторные id транзакций.
Сети офферволов
Если у вас сеть с множеством издателей, передавайте id аккаунтов с издателем и включите «Режим сети офферволов» в разделе «Сайты». Тогда аккаунты связываются только внутри одного издателя, ведь один человек нередко пользуется сайтами нескольких ваших издателей. Формат: publisher_id:user_id
Каталог офферов
POST /v1/offers отправляет названия ваших офферов (до 1 000 за вызов). Прошлые и будущие события будут показывать настоящее название, даже если в них только id.
Коды причин
Эти коды приходят в reasons[].code. Текст показывайте модераторам, а в логике используйте код.
| Код | Значение | Группа | Вес |
|---|---|---|---|
ip_tor | Выходной узел Tor | Сеть | 60 |
ip_vpn | VPN-подключение | Сеть | 45 |
ip_datacenter | IP дата-центра / хостинга | Сеть | 40 |
timezone_mismatch | Часовой пояс браузера не совпадает с местоположением IP | Сеть | 20 |
webrtc_ip_leak | Утечка реального IP из другой страны | Сеть | 30 |
ip_multi_timezone | Один IP с разными часовыми поясами (выход прокси) | Сеть | 50 |
proxy_farm_ip | Несколько аккаунтов на одном IP с одинаковым иностранным временем (общий прокси) | Сеть | 60 |
provider_rotation | Много интернет-провайдеров за несколько дней при иностранном времени (ротируемый резидентный прокси) | Сеть | 45 |
privacy_relay | iCloud Private Relay (функция приватности) | Сеть | 5 |
proxy_headers | Запрос пришёл через прокси | Сеть | 20 |
geo_sources_disagree | Источники геолокации расходятся | Сеть | 45 |
ip_shared_with_accounts | Тот же IP сегодня использовали другие аккаунты | Сеть | 25 |
country_changed | Сменилась страна | Сеть | 15 |
impossible_travel | Невозможное перемещение: IP двух далёких стран в течение нескольких часов | Сеть | 50 |
locale_country_mismatch | Язык браузера другой страны | Сеть | 30 |
phone_country_mismatch | Телефон из другой страны, чем IP | Сеть | 20 |
ip_flagged_on_network | IP заблокирован на других сайтах | Сеть | 25 |
device_shared_with_accounts | Устройство используется другими аккаунтами | Устройство | 55 |
automation_detected | Бот или средство автоматизации | Устройство | 70 |
auto_clicker | Автокликер на телефоне | Устройство | 55 |
scripted_behavior | Страницей управлял скрипт | Устройство | 45 |
emulator_or_vm | Эмулятор или виртуальная машина | Устройство | 45 |
browser_tampering | Подмена / спуфинг браузера | Устройство | 35 |
device_rooted | Телефон с root-доступом | Устройство | 25 |
incognito | Режим инкогнито | Устройство | 10 |
device_spoofed | Поддельная модель телефона | Устройство | 55 |
emulator_profile | Профиль эмулятора по умолчанию | Устройство | 60 |
cloud_phone | Облачный телефон | Устройство | 60 |
phone_farm_display | Экран фермы телефонов | Устройство | 45 |
device_market_mismatch | Телефон только для Китая за рубежом | Устройство | 25 |
storage_cleared_reappeared | Очистил cookies и вернулся | Устройство | 15 |
no_device_data | Данные устройства не переданы | Устройство | 15 |
request_reused | Токен устройства повторно использован другим аккаунтом | Устройство | 40 |
device_farm_proxy | Ферма телефонов на резидентных прокси | Устройство | 85 |
device_clones | Клоны устройства: один телефон сбрасывает свою личность, чтобы выдавать себя за много новых аккаунтов в одной сети | Устройство | 55 |
payout_address_shared | Адрес выплаты используют другие аккаунты | Аккаунт | 70 |
email_disposable | Одноразовый email | Аккаунт | 25 |
email_invalid | Адрес, на который не может прийти почта (такого почтового домена нет) | Аккаунт | 35 |
email_shared | Тот же email, что у другого аккаунта | Аккаунт | 50 |
phone_shared | Телефон общий с другими аккаунтами | Аккаунт | 50 |
phone_voip | VoIP или виртуальный номер | Аккаунт | 40 |
phone_invalid | Недействительный номер | Аккаунт | 30 |
many_devices_per_account | Слишком много устройств на одном аккаунте | Аккаунт | 20 |
flagged_on_network | Отмечен на других сайтах под защитой CashoutGuard | Аккаунт | 45 |
face_same_person | То же лицо, что у других аккаунтов | Аккаунт | 0 |
offer_completed_too_fast | Оффер выполнен нереально быстро | Поведение | 40 |
slow_after_click | Конверсия спустя долгое время после клика | Поведение | 0 |
conversion_velocity | Слишком много конверсий в час | Поведение | 30 |
conversion_without_click | Конверсия без клика с вашего offerwall | Поведение | 35 |
click_conversion_country_mismatch | Клик из одной страны, конверсия из другой | Поведение | 30 |
offer_country_mismatch | Страна оффера не совпадает со страной пользователя | Поведение | 45 |
click_velocity | Слишком много кликов по офферам за несколько минут | Поведение | 35 |
earnings_spike | Доход намного выше обычного пользователя | Поведение | 30 |
source_high_reversals | Источник с большим числом возвратов | Поведение | 45 |
offer_high_reversals | Оффер с большим числом возвратов | Поведение | 35 |
duplicate_transaction | Дублирующийся ID транзакции | Поведение | 60 |
offer_repeated_other_publisher | Тот же оффер уже выполнен через другого паблишера | Поведение | 85 |
offer_repeated_other_account | Тот же оффер уже выполнен другим аккаунтом на этом устройстве | Поведение | 85 |
offer_farm_burst | Волна фермы: много новых аккаунтов с фермерскими устройствами на этом оффере за несколько часов | Поведение | 30 |
signup_burst | Всплеск регистраций: несколько аккаунтов зарегистрировались за секунды с разных IP | Поведение | 25 |
face_check_required | Нужна проверка лица, ещё не пройдена | Поведение | 0 |
face_check_failed | Проверка лица не пройдена | Поведение | 0 |
publisher_farm_wave | Волна фермы его паблишера: большинство новых аккаунтов, которых он прислал на этот оффер, были с облачных телефонов или эмуляторов | Поведение | 55 |
cashout_velocity | Слишком много выводов в день | Поведение | 30 |
new_account_cashout | Вывод с совсем нового аккаунта | Поведение | 20 |
blocklisted | В вашем чёрном списке | Ваши списки | 100 |
Веса объединяются по принципу noisy-OR: score = 100 x (1 - произведение (1 - weight/100)). Два сигнала по 40 дают 64, а не 80. Совпадение с чёрным списком всегда даёт 100.
Webhooks
Укажите URL вебхука в панели. Мы отправляем POST на каждое событие с оценкой block (event.blocked), когда аккаунт переходит в block (account.blocked) и когда команда принимает решение по выводу (cashout.decided). Игнорируйте ненужные типы.
Каждый запрос подписывается вашим секретом webhook. HMAC-SHA256 от исходного тела в hex передаётся в заголовке X-CashoutGuard-Signature
Ответьте любым статусом 2xx. Неудачные доставки повторяются до 5 раз: через 10 секунд, 1 минуту, 5 минут и 15 минут.
Статус аккаунта
GET /v1/accounts/{account_id} возвращает текущую картину риска по аккаунту, не создавая событие.
POST /v1/accounts/{account_id}/status позволяет вашим собственным инструментам модерации менять статус аккаунта.
active: Обычная оценка.allowlisted: Оценка ограничена ниже порога проверки. Для доверенных пользователей и сотрудников.blocked: Любое событие возвращает block с оценкой 100.
Отмены и точность
POST /v1/outcomes сообщает, что стало с конверсиями после нашей оценки: отменены офферволлом или рекламодателем (chargeback) или подтверждены. Мы сопоставляем ваши id и измеряем движок на вашем трафике на странице «Точность».
Без кода: загрузите на странице «Точность» ежемесячный файл отмен от офферволлов (CSV или Excel). Колонку с id найдём сами.
PHP, Node.js, Python, Android
SDK не нужен. Достаточно обычного HTTP-запроса с коротким таймаутом. Полный пример для вывода:
- cashoutguard.php: PHP 7.4+, один файл, без Composer. Подключите через require.
- composer require cashoutguard/cashoutguard-php: тот же PHP-клиент с README для вашей папки vendor.
- npm install @cashoutguard/node: Node 18+, с типами TypeScript.
- cashoutguard-wordpress.zip: плагин WordPress: скрипт браузера на каждой странице, оценка регистраций и входов, а также хуки для выводов, конверсий офферволов и отмен.
Ошибки и лимиты
| HTTP | Тело | Значение |
|---|---|---|
| 401 | missing_or_invalid_secret_key | Секретный ключ отсутствует, неверен или отозван. В ответе указана причина в "reason" (missing, public_key, unknown_secret, placeholder, malformed) и способ исправить в "hint". |
| 404 | not_found | Аккаунт не найден (эндпоинты аккаунтов). |
| 422 | { "message", "errors" } | Некорректное тело запроса. В errors перечислены все поля. |
| 429 | Too Many Requests | Достигнут лимит запросов. Серверный API допускает 3 000 запросов в минуту на сайт. |
| 401 · agent.js | unknown_public_key | data-key не является публичным ключом ни одного сайта. Скопируйте pk_live_... заново в разделе «Установка». |
| 403 · agent.js | origin_not_allowed | Домена страницы нет в разрешённых источниках сайта. Добавьте его в разделе «Сайты» или оставьте список пустым. localhost работает всегда. agent.js также пишет об этом в консоли браузера. WebView в приложениях отправляют origin "null": добавьте null в список, чтобы принимать их. Нативному Android SDK ничего не нужно. |
| 403 | account_suspended | Аккаунт приостановлен, обычно из-за неоплаченного счёта. Проверьте раздел оплаты. |
| 503 | temporarily_unavailable | Кратковременный сбой на нашей стороне. Пропустите действие и повторите позже. |
Fail open: если вызов завершился таймаутом или ошибкой, разрешите действие и запишите это в лог. Никогда не позволяйте нашему сбою останавливать ваших пользователей.