Приняли криптоплатёж — приняли и его историю. Что показывает AML-скоринг кошелька
У криптоперевода нет банка, который проверит отправителя за вас. Проверка адреса до зачисления — единственный момент, когда решение ещё стоит один запрос, а не блокировку счёта.
Платёж пришёл в пятницу вечером: 42 000 USDT, адрес отправителя незнакомый, сумма совпадает со счётом до цента. Заказ закрыли, в понедельник склад отгрузил товар.
Через полтора месяца биржа, куда компания вывела эту выручку, заморозила аккаунт и попросила объяснить происхождение средств. Выяснилось, что монеты, которыми расплатился клиент, за два перевода до вас прошли через миксер. Клиент, скорее всего, и сам этого не знал: купил USDT у знакомого «по хорошему курсу». Но объясняться с биржей, писать пояснительную и ждать разморозки — не ему.
Обычный банковский платёж в такой ситуации выглядит иначе. Между вами и историей денег стоит банк отправителя: он проводит собственную проверку, он несёт часть ответственности, и если платёж дошёл — вопрос о происхождении к вам, как правило, уже не задают. В крипте этого посредника нет. Деньги приходят вместе со всей своей биографией, и с момента зачисления она становится вашей.
Точка, в которой это ещё можно остановить, ровно одна: до того, как вы зачислили платёж и отгрузили товар. Дальше блокчейн не откатывается.
Куда девается банк-посредник
Публичный адрес отправителя виден вам сразу — он лежит в самой транзакции. Что за ним стоит, не видно никак: адрес не подписан именем, не привязан к юрлицу и ничего не рассказывает о том, откуда на нём взялись монеты. Чтобы это узнать, нужно пройти по цепочке переводов назад и понять, что за кошельки в ней участвовали. Ручками это делают в обозревателе блокчейна, и на втором-третьем шаге человек обычно сдаётся: адресов становится десятки, а имён у них по-прежнему нет.
Атрибуция — это и есть то, за что платят в AML-скоринге. Не «показать транзакции» (их и так видно бесплатно), а сопоставить адреса с известными сущностями: вот это депозитный адрес лицензированной биржи, это — миксер, это — кошелёк из санкционного перечня, это — сборный адрес мошеннической схемы.
Деньги пришли — и история пришла вместе с ними
- Что происходит
- Адрес отправителя незнакомый. Откуда он получил монеты и через кого они шли до этого, никто не смотрел: транзакция подтверждена, сумма верная.
- Во что обходится
- Вопрос прилетает через недели, когда выручку выводят на биржу или в фиат: заморозка, объяснительная, в худшем случае удержание суммы до выяснения.
- Чем закрывается
- Скрининг адреса до зачисления возвращает разбивку источников средств по категориям (sourceOfFunds) и флаг санкционной экспозиции — до того, как товар уехал со склада.
Контрагент прошёл KYC, а платёж — нет
- Что происходит
- Клиент прислал паспорт, подписал документы и выглядит безупречно. Но платит он не со своего кошелька, а с адреса, у которого прямая связь с санкционным.
- Во что обходится
- Проверенным считается человек, а санкционным становится платёж. Для регулятора и для банка-эквайера это разные проверки, и вторую вы не делали.
- Чем закрывается
- Флаг sanctionsHit в ответе относится к конкретному адресу, а не к анкете клиента. Это отдельный вопрос, и задавать его нужно отдельно.
Проверка вручную стоит час и всё равно неполная
- Что происходит
- Комплаенс-офицер открывает обозреватель, кликает по входящим транзакциям, выписывает адреса в блокнот и пытается понять, чьи они.
- Во что обходится
- От 30 до 60 минут на один адрес — и в конце всё равно неизвестно, что кошелёк двумя переводами раньше был миксером: в обозревателе он выглядит как обычный адрес.
- Чем закрывается
- Один POST-запрос отдаёт балл, флаги, категории контрагентов и глубину связи (hops, isDirect) — результат воспроизводимый и его можно положить в лог как доказательство проверки.
Сколько стоит зачислить не тот платёж
Считать здесь надо не «экономию времени», а асимметрию. Проверка стоит один запрос к API. Не-проверка стоит ровно ноль — до того момента, когда она стоит всей суммы платежа плюс отгруженного товара плюс нескольких недель переписки с биржей.
| Смотрим в обозревателе руками | Скрининг по API | |
|---|---|---|
| Время на один адрес | 30–60 минут, и глубже двух-трёх переходов человек не идёт | Секунды; фактическое время приходит в поле elapsedMs |
| Что видно | Транзакции и суммы. Кому принадлежат адреса — нет | Категории контрагентов: биржа, миксер, даркнет, мошенничество, гэмблинг, P2P |
| Санкции | Сверять адрес со списками вручную | Отдельный флаг sanctionsHit — прямая и косвенная экспозиция |
| Воспроизводимость | Зависит от того, кто и насколько внимательно смотрел | Одинаковый JSON, который можно сохранить и предъявить |
| Цена | Час работы комплаенс-офицера | Один платный запрос, цена — на странице тарифов |
Отдельная строка расходов, о которой вспоминают поздно: у платёжного партнёра или банка, через который вы выводите крипту в фиат, есть собственный комплаенс. Один зафиксированный им инцидент — это не только эти конкретные деньги, но и разговор о том, останетесь ли вы клиентом.
Как читать балл риска
Сервис AML-скоринг принимает две вещи: публичный адрес кошелька и код сети. Приватный ключ и сид-фраза не нужны и не должны нигде фигурировать — адрес и так виден всем в блокчейне, а ключ не спрашивает ни один честный сервис.
Поддерживаются двенадцать сетей: BTC, ETH, BNB (BNB
Smart Chain), MATIC (Polygon), TRX (Tron), SOL
(Solana), LTC, XRP, BCH, DOGE, а также
USDT и USDC. Сеть указывается явно: один и тот же формат адреса
живёт в разных EVM-сетях, и угадывать за вас сервис не будет.
В ответе несколько слоёв, и читать их надо в правильном порядке.
riskScore — нормализованная оценка от 0 (риск не обнаружен) до 100
(максимальный). Это сводка по всей экспозиции: и по тому, откуда деньги приходили, и по тому,
куда уходили, и по контрагентам. Балл удобен для сортировки очереди на ручной разбор, но
строить бизнес-правило прямо на числе — плохая идея.
severity — то, на чём правило стоит строить. Пять значений:
UNKNOWN, LOW, MEDIUM, HIGH,
CRITICAL. Это человеко-понятная категория поверх балла. Мы намеренно не публикуем
таблицу «столько-то баллов = HIGH»: соответствие балла уровню задаёт источник данных, и
привязываться к конкретным числам — значит однажды получить сюрприз при обновлении их модели.
Уровень стабильнее числа.
sanctionsHit — отдельный булев флаг, и это самый сильный сигнал во всём
ответе. Он означает, что у адреса нашлась прямая или косвенная связь с санкционными
адресами и организациями. Не «высокий риск», а именно санкционная экспозиция, и обходиться с
ним нужно не как с числом, а как со стоп-краном.
pepCounterparty — связь с публичным должностным лицом. Это не запрет:
PEP имеет право платить за товар. Это повод включить усиленную проверку и посмотреть на
сделку внимательнее, чем обычно.
sourceOfFunds и destinationOfFunds — самое интересное
для человека, который потом будет писать пояснение. Это списки: откуда деньги приходили и
куда уходили, с разбивкой по категориям контрагентов (category: например,
exchange_licensed, mixer, darknet, scam,
sanctioned, gambling, p2p), долей
(percentage) и суммой в долларах (amountUsd). Здесь же
hops — число «прыжков» до контрагента, isDirect и
exposureType со значениями direct или indirect.
Разница между прямой и косвенной связью — это ровно та разница, из-за которой одинаковый балл значит разное. Кошелёк, который получил деньги напрямую с миксера, и кошелёк, у которого миксер нашёлся в четырёх переводах позади, — это разные разговоры с клиентом. Первый случай почти всегда осознанный. Второй нередко означает, что человек купил монеты на сомнительной площадке и понятия не имеет, что с ними было до него.
counterpartyConnections — список контрагентов с уровнем риска
(riskLevel: low, medium, high,
severe, unknown), категориями, оборотами в обе стороны
(receivedUsd, sentUsd) и страной. Плюс summary —
готовая строка вроде «Риск не обнаружен (0/100)», которую можно показать оператору как есть,
и dominantRiskCategory — категория с наибольшим вкладом в балл (или
null, если выраженной высокорисковой категории нет).
ALLOW, REVIEW, BLOCK и почему порог — ваш
Соблазн большой: взять из статьи в интернете «блокируем всё выше 70» и вписать в код. Так делать не надо, и вот почему.
Порог — это функция от вашего среднего чека, обратимости сделки и аппетита к риску. Магазин
цифровых товаров со средним чеком в три тысячи рублей может спокойно пропускать
MEDIUM: цена ошибки — стоимость одной лицензии, а каждый ручной разбор дороже
товара. Обменник, где средний чек — два миллиона и деньги уходят необратимо, отправляет на
ручную проверку уже MEDIUM, а на HIGH останавливает сделку целиком.
Оба правы. Единственного правильного числа тут не существует, есть ваша риск-политика.
Что стоит зафиксировать письменно, до того как писать код:
- Какие исходы ведут к BLOCK без вариантов (санкционная экспозиция почти у всех попадает сюда).
- Какие уходят в REVIEW — очередь ручного разбора, и кто в ней работает.
- Что делать с UNKNOWN и с недоступностью источника: это «нет данных», а не «чисто».
- Сколько времени вы держите платёж в ожидании решения и что говорите клиенту в это время.
Четвёртый пункт забывают чаще всего, а он и ломает процесс: адрес заскорился как рискованный, а что делать с деньгами, которые уже в блокчейне и назад не отзываются, никто не решил. Отправить обратно? На тот же адрес, который вы считаете рискованным? Заморозить до выяснения и запросить у клиента документы? Это должно быть написано до первого срабатывания, а не придумываться в пятницу вечером.
Код: проверка адреса до зачисления
Запрос ровно один: POST /api/aml/screen с телом из двух полей. Скопируйте и
запустите — демо-ключ публичный, регистрация не нужна.
curl -X POST "https://atlorium.com/api/aml/screen" \
-H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
-H "Content-Type: application/json" \
-d '{"walletAddress":"0x26a5919cd0392df875a53873b0323c42094bb216","blockchain":"BNB"}'
import requests
resp = requests.post(
"https://atlorium.com/api/aml/screen",
json={
"walletAddress": "0x26a5919cd0392df875a53873b0323c42094bb216",
"blockchain": "BNB",
},
headers={"Authorization": "Bearer ak_sandbox_demo_mockdata_v1"},
timeout=20,
)
# 503 — источник недоступен. Деньги за такой запрос не списываются:
# платёж не зачисляем и не отклоняем, а откладываем и повторяем позже.
if resp.status_code == 503:
raise RuntimeError("Скоринг недоступен — платёж остаётся в ожидании")
data = resp.json()
# Пороги ниже — ПРИМЕР. Свои возьмите из риск-политики, а не отсюда.
if data["sanctionsHit"]:
decision = "BLOCK"
elif data["severity"] in ("HIGH", "CRITICAL"):
decision = "BLOCK"
elif data["severity"] in ("MEDIUM", "UNKNOWN") or data["pepCounterparty"]:
decision = "REVIEW"
else:
decision = "ALLOW"
print(decision, data["riskScore"], data["summary"])
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer ak_sandbox_demo_mockdata_v1");
var body = new StringContent(
"""{"walletAddress":"0x26a5919cd0392df875a53873b0323c42094bb216","blockchain":"BNB"}""",
Encoding.UTF8, "application/json");
var response = await http.PostAsync("https://atlorium.com/api/aml/screen", body);
// Сбой источника: списания не было, решение по платежу не принимаем.
if ((int)response.StatusCode == 503)
throw new InvalidOperationException("Скоринг недоступен — повторим позже");
using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var root = doc.RootElement;
var severity = root.GetProperty("severity").GetString();
var sanctions = root.GetProperty("sanctionsHit").GetBoolean();
var pep = root.GetProperty("pepCounterparty").GetBoolean();
// Пороги — пример. Настоящие живут в вашей риск-политике.
var decision = sanctions || severity is "HIGH" or "CRITICAL"
? "BLOCK"
: severity is "MEDIUM" or "UNKNOWN" || pep
? "REVIEW"
: "ALLOW";
{
"walletAddress": "0x26a5919cd0392df875a53873b0323c42094bb216",
"blockchain": "BNB",
"screeningType": "WALLET_SCREENING",
"riskScore": 0,
"severity": "LOW",
"status": "SCREENED",
"summary": "Риск не обнаружен (0/100)",
"dominantRiskCategory": null,
"sanctionsHit": false,
"pepCounterparty": false,
"totalValueUsd": 184305.71,
"feeUsd": 42.18,
"sourceOfFunds": [],
"destinationOfFunds": [
{
"category": "exchange_licensed",
"percentage": 100,
"amountUsd": 184305.71,
"entityName": "Kessler-Waters Exchange",
"entityType": "Exchange",
"entitySubtype": "Licensed / KYC",
"hops": 1,
"description": "Exchange / Licensed",
"isDirect": true,
"exposureDirection": "outgoing",
"exposureType": "direct",
"country": "MT"
}
],
"counterpartyConnections": [
{
"entityName": "Kessler-Waters Exchange",
"entityType": "Exchange",
"riskLevel": "low",
"categories": ["exchange"],
"percentage": 100,
"isDirect": true
}
],
"elapsedMs": 3
}
Тот же вызов на шести языках (Python, TypeScript, Go, Java, C#, PHP) — в репозитории aml-crypto-screening-api-client. Примеры запускаются сразу: ключ в них уже стоит демонстрационный.
Это ответ песочницы: демо-ключ отдаёт моки — данные правдоподобные, но сгенерированные, и на один и тот же запрос всегда приходит один и тот же ответ. Формат настоящий, содержимое — нет, и писать на нём тесты удобно именно поэтому. Настоящий скоринг приходит с личным ключом.
Ответ с найденным риском выглядит так же по структуре, но заполнены другие поля. Значения ниже придуманы для иллюстрации — набор полей настоящий.
curl -X POST "https://atlorium.com/api/aml/screen" \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"walletAddress":"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa","blockchain":"BTC"}'
{
"walletAddress": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"blockchain": "BTC",
"riskScore": 78,
"severity": "HIGH",
"status": "SCREENED",
"summary": "Высокий риск (78/100)",
"dominantRiskCategory": "mixer",
"sanctionsHit": false,
"pepCounterparty": false,
"totalValueUsd": 61240.00,
"sourceOfFunds": [
{
"category": "mixer",
"percentage": 46.2,
"amountUsd": 28293.0,
"entityType": "Mixer",
"hops": 2,
"isDirect": false,
"exposureDirection": "incoming",
"exposureType": "indirect",
"country": null
},
{
"category": "p2p",
"percentage": 53.8,
"amountUsd": 32947.0,
"entityType": "P2P",
"hops": 1,
"isDirect": true,
"exposureDirection": "incoming",
"exposureType": "direct"
}
],
"counterpartyConnections": [
{
"entityName": "unknown",
"entityType": "Mixer",
"riskLevel": "high",
"categories": ["mixer"],
"receivedUsd": 28293.0,
"percentage": 46.2,
"isDirect": false
}
]
}
Тот же вызов на шести языках (Python, TypeScript, Go, Java, C#, PHP) — в репозитории aml-crypto-screening-api-client. Примеры запускаются сразу: ключ в них уже стоит демонстрационный.
Что здесь видит комплаенс-офицер: санкций нет, но почти половина входящих средств пришла с
миксера через два перевода (exposureType: indirect, hops: 2).
Автоматически это BLOCK далеко не у всех — но в REVIEW уходит
у всех.
Сводная таблица исходов, из которой удобно собрать своё правило:
| Что в ответе | Что это значит | Типичное решение |
|---|---|---|
sanctionsHit: true |
Прямая или косвенная связь с санкционными адресами | BLOCK + эскалация. Автоматически пропускать нельзя |
severity: CRITICAL / HIGH |
Высокая экспозиция на рисковые категории | BLOCK или REVIEW — по вашей политике, но не ALLOW |
severity: MEDIUM |
Риск есть, но не доминирует | REVIEW; смотреть dominantRiskCategory и exposureType |
pepCounterparty: true |
Среди контрагентов есть публичное должностное лицо | Усиленная проверка. Само по себе не запрет |
severity: UNKNOWN или status ≠ SCREENED |
Данных не хватило | Не ALLOW. Это «неизвестно», а не «чисто» |
severity: LOW, dominantRiskCategory: null |
Связей с рисковыми категориями не найдено | ALLOW — и всё равно сохраните ответ в журнал |
HTTP 503 |
Источник недоступен, скрининга не было | Платёж в ожидание, повтор позже. Деньги не списаны |
Второй эндпоинт, GET /api/aml/validate?walletAddress=…&blockchain=…, проверяет
только формат: поддерживается ли сеть и правдоподобен ли адрес (для EVM-сетей — «0x» и 40
шестнадцатеричных символов, для остальных — длина от 16 до 128 знаков без пробелов).
Существование адреса в блокчейне он не проверяет и в скоринг не ходит. Учтите: этот вызов
тоже проходит через общий учёт запросов, так что если вам нужна просто проверка формы поля в
интерфейсе — дешевле сделать её у себя, регуляркой.
Чего скоринг не делает
Раздел, ради которого стоит читать всю статью. Если продавец AML-инструмента такого раздела не пишет — спросите его сами.
Не выносит юридическую квалификацию. Ответ не устанавливает, что средства получены
преступным путём, и не является заключением. Это оценка связей адреса с категориями
контрагентов по данным атрибуции. Вменять что-либо человеку на основании поля
severity нельзя ни вам, ни нам.
Не гарантирует полноту санкционных списков. Атрибуция адресов — работа внешнего
источника, а санкционное лицо может завести новый кошелёк сегодня утром, и размечен он будет
не сегодня. sanctionsHit: false означает «связь не найдена», а не «связи нет».
Отсутствие флага — не индульгенция и не защита от претензии регулятора.
Проверяет адрес, а не человека. Кто держит приватный ключ, из блокчейна не видно. Если клиент платит с депозитного адреса биржи, вы увидите биржу, а не его. AML-скоринг не заменяет KYC — он отвечает на другой вопрос и стоит рядом с ним, а не вместо.
Не отменяет транзакцию. Никакой сервис не умеет отозвать перевод в блокчейне. Всё, что даёт проверка, — возможность не зачислить платёж в свой учёт и не отгрузить товар. Ровно поэтому проверка до зачисления и стоит один запрос, а после — уже совсем других денег.
Это снимок, а не пропуск навсегда. Кошелёк живёт дальше: сегодня LOW,
завтра он получил монеты с миксера и стал HIGH. Скринить надо на момент
зачисления каждого платежа, а сохранённый ответ ценен как датированная запись в журнале, а не
как вечное разрешение для этого адреса.
Двенадцать сетей, и только они. Сеть вне списка — ответ 400, а не пустой
результат. Это сделано намеренно: молчаливый «нулевой риск» по непроверенной сети опаснее
честной ошибки.
Частые вопросы
Частые вопросы
Что показывает AML-проверка криптокошелька?
По публичному адресу и сети сервис возвращает балл риска от 0 до 100, уровень серьёзности (UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL), флаги санкционной экспозиции и связи с публичным должностным лицом, разбивку источников и назначения средств по категориям контрагентов (биржи, миксеры, даркнет, мошенничество, гэмблинг, P2P) и список контрагентов с их уровнем риска и оборотами.
Нужен ли приватный ключ или сид-фраза для проверки?
Нет, и вводить их нельзя нигде и никогда. Для скрининга нужен только публичный адрес кошелька — тот, который отправитель и так сообщает вам вместе с платежом и который виден всем в блокчейне.
Какой балл риска считать опасным?
Универсального порога не существует, и любая статья, которая называет вам число, вводит в заблуждение. Порог зависит от среднего чека, обратимости сделки и вашей риск-политики: магазин с чеком в три тысячи рублей и обменник с чеком в два миллиона примут разные решения при одном и том же балле. Опираться удобнее на уровень severity, а не на число, и фиксировать пороги письменно.
Значит ли высокий риск, что деньги преступные?
Нет. Сервис показывает связи адреса с рисковыми категориями контрагентов, а не устанавливает факт преступления и не даёт юридическую квалификацию. Решение по платежу принимает комплаенс-офицер: ответ API — это материал для решения и доказательство того, что проверка была сделана.
Какие блокчейны поддерживаются?
Двенадцать сетей: Bitcoin, Ethereum, BNB Smart Chain, Polygon, Tron, Solana, Litecoin, XRP Ledger, Bitcoin Cash, Dogecoin, а также USDT и USDC. Сеть указывается в запросе явно; если её нет в списке, сервис вернёт ошибку 400, а не пустой результат.
Есть ли бесплатный лимит у AML-скоринга?
Нет. Каждый скрининг тарифицируется с первого запроса — за ним стоит платный вызов внешнего источника. Подписки при этом нет: платите ровно за сделанные проверки. Некорректный ввод (ошибка 400) и недоступность источника (ошибка 503) не списываются.
Сервисы из этой статьи
AML-скрининг крипто-кошелька: балл риска, санкции, источники и назначение средств, контрагенты
Попробовать прямо сейчас — без регистрации
Демо-ключ ak_sandbox_demo_mockdata_v1 — публичный и общий для всех.
С ним API отвечает моками: данные правдоподобные, но сгенерированные,
и они не меняются от запроса к запросу — на них удобно писать тесты.
Настоящие данные приходят с личным ключом.
curl -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
https://atlorium.com/openapi/aml_ru.json