Data API (ранее описанный здесь как Server API) позволяет вашему бэкенду читать данные идентификации, которые TRACIO уже собрал для вашего workspace: историю посетителя, отдельные сессии и счётчики активности за короткое окно. Несколько маршрутов также пишут — записи аккаунтов, пользовательские поля и элементы списков, — чтобы ваш бэкенд мог дополнять то, что известно TRACIO.
Он дополняет Webhooks, а не заменяет их:
| Webhooks | Data API | |
|---|---|---|
| Направление | TRACIO отправляет на ваш эндпоинт | Ваш бэкенд запрашивает сам, когда нужно |
| Момент | В момент каждой идентификации | В любое время, в пределах вашего окна хранения |
| Лучше для | Реакции на событие | Поиска данных в момент решения, дозагрузок, разбора инцидентов |
Обе поверхности доступны с тарифа Pro и выше.
https://api.tracio.ai/v2Это отдельный хост — не тот, что у браузерного эндпоинта (edge.tracio.ai), и не
тот, что у дашборда (app.tracio.ai). Все три разные: браузер общается с edge вашим
публичным ключом, ваш бэкенд общается с Data API вашим секретным ключом.
Маршруты /v1 удалены. Используйте /v2: маршруты те же, а каждый ключ JSON и
каждый параметр запроса записан в snake_case (visitor_id, risk_score,
min_risk_score). Ключи внутри ваших собственных словарей fields и labels — это
ваши данные, и они сохраняют исходное написание. JavaScript SDK сохраняет свой
интерфейс в camelCase.
Каждый запрос несёт ваш секретный ключ как bearer-токен:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Data API работает только в режиме server-to-server. Заголовки CORS намеренно не возвращаются, поэтому браузер не может его вызвать — именно это удерживает ваш секретный ключ вне клиентского кода. Никогда не отдавайте секретный ключ в браузер.
Создайте его в дашборде, в разделе API Keys, выбрав тип secret.
tracio_sk_ плюс 43 символа, всего 53. Дашборд
показывает его по первым нескольким символам, чтобы вы могли отличать ключи
друг от друга.404, ровно как для несуществующих данных.Ротация выпускает новый ключ и оставляет старый рабочим ещё 7 дней, так что вы можете выкатить замену без простоя. Задеплойте новый ключ, убедитесь, что трафик перешёл на него, и дайте старому истечь. Публичные ключи не ротируются — они не секреты и по замыслу видны в исходном коде вашей страницы.
| Метод | Путь | Возвращает |
|---|---|---|
GET | /v2/visitors/{visitor_id} | Агрегированную историю одного посетителя плюс его последнюю сессию |
GET | /v2/visitors/{visitor_id}/sessions | Постраничный список сессий этого посетителя |
GET | /v2/visitors/{visitor_id}/sessions/latest | Одну самую свежую сессию |
GET | /v2/visitors/{visitor_id}/velocity | Счётчики активности за короткое окно |
GET | /v2/sessions/{request_id} | Одну сессию по идентификатору её запроса |
POST | /v2/accounts/metadata | Привязывает отображаемые имена и email к вашим идентификаторам аккаунтов |
POST | /v2/events/fields | Прикрепляет пользовательские поля к визиту после загрузки страницы |
POST | /v2/events/fields/delete | Стирает пользовательские поля визита — сам визит не затрагивается |
GET | /v2/lists | Списки вашего workspace: id, имя, тип (действие), режим, число элементов |
POST | /v2/lists/{list_id}/entries | Добавляет значение в список, при желании с TTL |
POST | /v2/lists/{list_id}/entries/delete | Удаляет значение из списка |
GET | /.well-known/webhook-keys | Публичные ключи для подписи платформы у вебхуков (без аутентификации) |
Тот же ключ и тот же хост обслуживают и API решений, целиком в snake_case:
| Метод | Путь | Назначение |
|---|---|---|
POST | /v2/transactions | Синхронное решение APPROVE / REVIEW / DECLINE по бизнес-событию — Business и выше, кроме партнёрской конверсии (account_register или deposit с affiliate_id), доступной на любом тарифе с доступом к API |
GET | /v2/transactions/{transaction_id} | Повторно прочитать решение вместе с его меткой и действующим состоянием — Business и выше, кроме транзакции, атрибутированной партнёру |
PUT, DELETE | /v2/transaction-labels | Установить или снять метку исхода у транзакций, до 50 за раз — Business и выше |
GET | /v2/transaction-labels/value-set | Допустимые значения меток с их полярностью — Business и выше |
POST | /v2/logins/assess | Оценить вход: allow / challenge / review / deny с причинами |
POST, GET | /v2/accounts/{user_id}/trusted-devices | Сделать устройство визита доверенным для аккаунта после вашей дополнительной проверки или получить список доверенных устройств аккаунта |
DELETE | /v2/accounts/{user_id}/trusted-devices/{device_ref} | Отозвать доверие к устройству |
GET | /v2/affiliates/{affiliate_id} | Отчёт о качестве партнёра за 90 дней с рекомендацией hold / review |
POST, GET | /v2/self-exclusions | Установить или продлить самоисключение для аккаунта либо получить их список |
DELETE | /v2/self-exclusions/{user_id} | Досрочно снять самоисключение |
Их результаты приходят к вам и как события вебхуков:
transaction_state_changed, login_risk, trusted_device_enrolled,
trusted_device_revoked и affiliate_threshold_crossed.
Завершающий слеш принимается и игнорируется. Неизвестный путь или неверный метод возвращают тот же JSON-конверт ошибки, что и всё остальное, — никогда не HTML- и не текстовую страницу.
Любое чтение ограничено временным окном, которым управляют два необязательных параметра запроса:
| Параметр | Принимает |
|---|---|
from | YYYY-MM-DD или полный таймстамп RFC 3339 |
to | YYYY-MM-DD или полный таймстамп RFC 3339 |
to включает весь этот день целиком.400 invalid_request и сообщением
time must be YYYY-MM-DD or RFC3339.meta, поэтому
проверяйте meta.from и meta.to, а не считайте, что ваш запрос выполнен буквально.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Ответ несёт агрегированную историю и включает в себя последнюю сессию, поэтому в типичном случае хватает одного запроса, а не двух:
{ "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "first_seen_at": "2026-05-02T10:11:12Z", "last_seen_at": "2026-07-25T08:00:00Z", "visits": 42, "incognito_visits": 3, "unique_ips": 5, "unique_countries": 2, "browsers": ["Chrome"], "os": ["macOS"], "devices": ["desktop"], "risk": { "max_risk_score": 63, "avg_bot_score": 12.5, "bot_sessions": 7, "last_decision": "real" }, "network": { "vpn_seen": false, "proxy_seen": false, "tor_seen": false, "datacenter_seen": true, "proxy_detected_seen": true, "last_isp": "Deutsche Telekom", "last_real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" } }, "last_session": { "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "account_id": "user_8842", "account": { "id": "user_8842", "email": "ann@example.com", "display_name": "Ann Lee" }, "timestamp": "2026-07-25T08:00:00Z", "tag": "checkout", "fields": { "order_ref": "A-1029" }, "url": "https://shop.example.com/checkout", "ip": "203.0.113.42", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", "browser": { "name": "Chrome", "version": "126.0" }, "os": { "name": "macOS", "version": "14.5" }, "device": "desktop", "gpu": "Apple M2", "geo": { "country": "DE", "city": "Berlin", "timezone": "Europe/Berlin", "isp": "Deutsche Telekom" }, "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "connection_type": "residential", "proxy_detected": true, "real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" }, "asn": 3320 }, "screen": { "width": 2560, "height": 1600, "color_depth": 30, "pixel_ratio": 2 }, "locale": { "languages": ["en-US", "de"], "timezone": "Europe/Berlin" }, "client_hints": { "architecture": "arm", "bitness": "64", "platform_version": "14.5.0" }, "extensions": [ { "slug": "ublock-origin", "name": "uBlock Origin", "category": "adblock", "risky": false, "store_url": "https://chromewebstore.google.com/detail/cjpalhdlnbpafiamejdnhcphjbkeiagm" } ], "bot": { "result": "human", "score": 4.5, "antidetect_score": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "match_type": "exact", "match_confidence": 0.99 }, "device_info": { "device_id": "d_4f9c2e", "cross_browser": true, "confidence": 0.88, "linked_browsers": 2 }, "decision": { "action": "real", "risk_score": 12, "suspect_score": 3 } }, "meta": { "plan": "business", "retention_days": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| Поле | Значение |
|---|---|
visits, incognito_visits | Всего визитов в окне и сколько из них были в приватном окне |
unique_ips, unique_countries | Различные адреса и страны, встреченные в окне |
browsers, os, devices | Различные окружения, в которых появлялся этот посетитель |
risk.max_risk_score | Наивысшая оценка риска, зафиксированная в окне, 0..100 |
risk.last_decision | Решение, зафиксированное для самого свежего визита |
risk.avg_bot_score, risk.bot_sessions | Средняя оценка бота и число ботовых сессий — Business и выше |
network.*_seen | Встречались ли когда-либо у этого посетителя VPN, прокси, выходной узел Tor или адрес ЦОД |
network.proxy_detected_seen | Выходил ли хотя бы один визит в окне через прокси или VPN перед браузером — см. network.proxy_detected в разделе Факты об устройстве |
network.last_isp | Самый свежий ISP — Business и выше |
network.last_real_ip | Самый свежий адрес, наблюдавшийся за прокси или VPN (address, country, isp), — Business и выше; отсутствует, когда ни один визит его не принёс |
last_session | Полный объект сессии для самого свежего визита |
meta | Тариф, его срок хранения в днях и фактически применённое окно |
Посетитель, по которому нет данных внутри окна хранения, отдаёт 404 not_found с
сообщением visitor not found in the retention window — это не ошибка вашей
интеграции, а признак того, что посетитель новый или уже вышел за срок хранения.
Сессия несёт два вердикта, и они отвечают на разные вопросы — был ли клиент автоматизирован и к какому общему выводу пришёл движок риска:
| Поле | Значения |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Присутствует, когда bot.result равно bot: либо конкретный инструмент (playwright, puppeteer, selenium, jsdom, claude_computer_use…), либо проверенный ИИ-агент (ai_agent:<vendor>), либо семейство, когда инструмент не назван, — automation, headless, antidetect, extension, privacy_browser, impersonation, other |
decision.action | real, fake, suspicious |
bot.score и decision.risk_score оба идут в шкале 0..100. На Business и выше
guidance превращает их в рекомендации по каждому сценарию на лестнице
allow → challenge → review → deny — что означает каждая ступень, см. в разделе
Guidance. Значения и шкалы совпадают с
телом вебхука v3 для того же визита, а guidance вычисляется по тем же правилам.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&min_risk_score=50"| Параметр | По умолчанию | Примечания |
|---|---|---|
limit | 50 | Ограничен сверху 500; большее значение подрезается, а не отклоняется |
from, to | Срок хранения тарифа | Общее временное окно, описанное выше |
cursor | — | Непрозрачный курсор пагинации с предыдущей страницы |
bot_result | — | Оставить только сессии с этим вердиктом бота |
min_risk_score | — | Оставить только сессии с оценкой риска не ниже этой, 0..100 |
Сессии возвращаются начиная с самых свежих:
{ "items": [ { "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "timestamp": "2026-07-25T08:00:00Z" } ], "next_cursor": "MTcyMTg5NDQwMDAwMDphYmMxMjM", "has_more": true, "meta": { "plan": "business", "retention_days": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}Постраничная выборка построена на курсорах. Параметров page или offset нет:
передайте полученный next_cursor обратно как cursor и продолжайте, пока has_more
равно true. На последней странице next_cursor может вовсе отсутствовать, поэтому не
делайте это поле обязательным.
async function allSessions(visitorId: string, secretKey: string) { const sessions = [] let cursor: string | undefined
do { const url = new URL(`https://api.tracio.ai/v2/visitors/${visitorId}/sessions`) url.searchParams.set("limit", "500") if (cursor) url.searchParams.set("cursor", cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${secretKey}` } }) if (!res.ok) throw new Error(`Data API: ${res.status}`)
const page = await res.json() sessions.push(...page.items) cursor = page.has_more ? page.next_cursor : undefined } while (cursor)
return sessions}Считайте курсор непрозрачным — его содержимое является деталью реализации и может
измениться. Отредактированный курсор отклоняется с 400 invalid_request и сообщением
malformed cursor.
Помимо браузера и ОС, взятых из User-Agent, сессия несёт то, что браузер посетителя сообщает о машине, очищенное на нашей стороне. Каждое поле отсутствует, когда визит таких данных не принёс, поэтому считайте любое из них необязательным.
| Поле | Значение |
|---|---|
gpu | Модель видеоадаптера, как её сообщает браузер (WebGL), нормализованная до читаемого имени — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer означает, что настоящего GPU нет (виртуальная машина или headless-окружение); Safari сообщает Apple GPU |
network.proxy_detected | HTTP-трафик визита и его сырые сетевые пути выходят через разные сети — перед браузером стоит прокси или VPN; два адреса одного и того же провайдера (NAT оператора, второй выход того же VPN) не считаются; флаг не выставляется, когда адреса невозможно сравнить (iCloud Private Relay, IPv4 рядом с IPv6) |
network.real_ip.address, .country, .isp | Публичный адрес, наблюдаемый на сыром сетевом пути, то есть адрес за прокси или VPN, вместе с его страной и ISP — Business и выше; отсутствует, когда такого адреса не наблюдали (country и isp отсутствуют, когда их не удалось определить) |
device_info.device_id, .cross_browser, .confidence, .linked_browsers | Присутствует, когда идентичность устройства удалось определить: стабильный идентификатор физического устройства, общий для браузеров на нём, пришёл ли этот визит через другой браузер, чем прежде, уверенность этого совпадения и сколько различных посетителей (браузеров) делят устройство — больше единицы означает одну машину под несколькими браузерными идентичностями — Business и выше |
os_environment | Окружение рабочего стола, измеренное на машине с Linux (Mint 22+, Ubuntu 25.04+, Ubuntu, GNOME, KDE) — Business и выше; отсутствует, когда не определено |
spoofing | Что визит заявил против того, что измерили независимые проверки (claimed, real, spoofed_axes из os, gpu, screen, network, browser; anonymous_browser с названиями продуктов) — Business и выше; присутствует только тогда, когда подмена обнаружена |
screen.width, .height, .color_depth, .pixel_ratio | Разрешение экрана, глубина цвета и device pixel ratio, как их сообщает браузер — Business и выше |
locale.languages, locale.timezone | Собственные предпочитаемые языки браузера и его таймзона — в отличие от geo.timezone, которая выводится из IP-адреса; расхождение между ними — частый признак подменённого местоположения — Business и выше |
client_hints.architecture, .bitness, .model, .device_name, .platform_version | User-Agent Client Hints: архитектура и разрядность процессора, код модели устройства (Android, например SM-A556B) вместе с её маркетинговым именем из списка устройств Google Play (device_name, например Samsung Galaxy A55 5G) и точная версия платформы; только браузеры на Chromium — Business и выше |
environment.virtual_machine, environment.hypervisor | Присутствует только тогда, когда видеоадаптер сам представился виртуальным; hypervisor — закрытый словарь (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Отсутствие блока означает, что таких свидетельств нет — Business и выше |
extensions перечисляет расширения браузера, обнаруженные во время визита, — Pro и выше.
Каждый элемент — объект:
| Поле | Значение |
|---|---|
slug | Стабильный машинный идентификатор расширения, то же значение, которое доставляет вебхук |
name | Читаемое человеком название |
category | Укрупнённый класс — adblock, privacy, automation, wallet, vpn, devtools, other и так далее |
risky | true для расширений, связанных с автоматизацией, подменой или кражей учётных данных |
store_url | Ссылка на карточку расширения в магазине, когда она известна |
Находка сообщается только после того, как прошла наши проверки доверия, — окружение, которое отвечает «установлено» на любую пробу, или партия длиннее двенадцати имён отбраковываются как ненадёжные. Поэтому пустой или отсутствующий список означает «ничего, что мы смогли подтвердить», а не «расширений не установлено». Читайте его как свидетельство, а не как инвентарный список.
fields содержит пользовательские пары ключ–значение, которые ваша интеграция
прикрепила к визиту, — номер заказа, название тарифа, результат оплаты. Ключи и
значения всегда строки: прикреплённое число или логическое значение возвращается в
текстовом виде. Как и остальная сессия, поле доступно на любом тарифе с доступом к API
(Pro и выше) и отсутствует, если у визита нет полей.
fields читается заново при каждом запросе, а не копируется в момент визита. Поля,
прикреплённые после визита (см. Прикрепление полей после визита),
тоже попадают сюда, поэтому для одного и того же визита fields может содержать
больше, чем доставил вебхук.
Не кладите в пользовательские поля персональные данные — имена, адреса электронной почты, номера телефонов: вместо них прикрепляйте идентификатор, который распознают ваши собственные системы, а то, кто стоит за аккаунтом, передавайте записью аккаунта (ниже).
account — это ваша запись об аккаунте, стоящем за визитом, — то, что вы последним
отправили для этого account_id через POST /v2/accounts/metadata:
{ "account": { "id": "user-42", "email": "ann@example.com", "display_name": "Ann Lee" }}| Поле | Описание |
|---|---|
id | Ваш идентификатор аккаунта — то же значение, что account_id |
email | Адрес email, который вы отправили для аккаунта; нет, если пуст |
display_name | Отображаемое имя, которое вы отправили; нет, если пусто |
Запись читается заново при каждом запросе, поэтому переименование видно и на старых визитах: блок описывает аккаунт таким, какой он сейчас, а не каким он был в момент визита. Он доступен на любом тарифе с доступом к API (Pro и выше). Метки, которые вы отправили вместе с записью, не возвращаются.
Блок отсутствует, когда у визита нет связанного аккаунта, когда записи для него нет (или вы очистили её, отправив пустые значения), и — редко — когда запись не удалось прочитать: сессия всё равно возвращается целиком, а запрос не падает, поэтому считайте отсутствующий блок «неизвестно», а не «у этого аккаунта нет email». Вебхуки этот блок не несут.
Самая свежая:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Возвращается голый объект сессии — не массив и не обёрнутый в конверт. Посетитель без
сессий в окне отдаёт 404 not_found с сообщением
no sessions for this visitor in the retention window.
Либо по request_id — идентификатору, который встречается и в payload вебхука:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitor_id=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitor_id здесь необязателен, но если вы его знаете, передача заметно ускоряет
поиск.
identification.drift_score — сглаженная поведенческая оценка, на которой построен
вердикт о захвате аккаунта, Business и выше, — возвращается только этим чтением одной
сессии. Поведенческий дрейф измеряется во время визита, уже после первичной
идентификации, поэтому маршруты списка и последней сессии его опускают. У визита,
поведение которого так и не было оценено, drift_score нет. То же значение приходит к
вам через вебхук account_takeover.
Вебхук может доставить визит больше одного раза: сначала primary, затем late или
correction по мере поступления более медленных сигналов и поведенческих
свидетельств, — и более поздняя фаза может перевернуть прежний вердикт. Data API
отвечает по записи primary. Более поздние фазы в неё не вливаются, поэтому для
исправленного визита последний вебхук — более новый ответ. Если вы действуете через
несколько минут после визита, предпочитайте фазу, полученную последней, свежему
чтению здесь.
Одно работает в обратную сторону: как только детектор захвата аккаунта сработал для
визита, guidance здесь это отражает, тогда как тела вебхука identification того же
визита эту ось не несут — она приходит отдельной доставкой account_takeover.
Velocity отвечает на вопрос «сколько этот посетитель успел наделать за последнее время» — именно так выглядят перебор учётных данных, тестирование карт и массовые регистрации.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window принимает 1h, 24h или 7d, по умолчанию — 24h. Любое другое значение
отклоняется с 400 invalid_request и сообщением
window must be one of: 1h, 24h, 7d.
{ "window": "1h", "events": 37, "unique_ips": 9, "unique_countries": 3, "unique_accounts": 12, "bot_events": 4, "meta": { "plan": "business", "retention_days": 90, "from": "2026-07-25T11:00:00Z", "to": "2026-07-25T12:00:00Z" }}unique_accounts считает различные значения linkedId, которые вы отправили для
этого устройства, — см. Связывание аккаунтов. bot_events
доступно на Business и выше; на Pro ключ отсутствует.
TRACIO знает ваши аккаунты только по идентификатору, который ваш сайт передаёт как
linkedId, — непрозрачной строке вроде usr_8412. Отправьте человекочитаемые метки
один раз, и каждый экран дашборда, где показан этот аккаунт, покажет имя или email
вместо сырого идентификатора, а объект сессии вернёт их в своём блоке
account. Ничто здесь не влияет на детекцию и оценку — это слой
отображения.
curl -s -X POST "https://api.tracio.ai/v2/accounts/metadata" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"accounts":[ {"account_id":"usr_8412","email":"ann@example.com","display_name":"Ann Lee","labels":{"tier":"vip"}}, {"account_id":"usr_9130","display_name":"Bob Ray"} ]}'{ "updated": 2 }fields. Email или имя,
отправленные сюда, живут только в записи аккаунта вашего workspace: они не хранятся
для каждого визита, не пишутся в страницу, а одна отправка обновляет каждый визит
этого аккаунта.400 invalid_request, и один неверный элемент
отклоняет весь пакет — ошибка называет его позицию. Повторяющиеся идентификаторы
аккаунтов в одном пакете допустимы: побеждает последний.Лимиты: 1000 аккаунтов на запрос, тело 1 МиБ, account_id ≤ 256 символов,
email ≤ 320, display_name ≤ 256, не больше 16 меток с ключами ≤ 64 и значениями
≤ 256 символов.
Браузер может отправить только то, что страница знает в момент рендера, — номер заказа, но никогда не результат проверки платежа или что-то ещё, что решается позже. Этот маршрут закрывает пробел: найдите визит по полю, которое у него уже есть, и прикрепите к нему дополнительные поля на стороне сервера.
curl -s -X POST "https://api.tracio.ai/v2/events/fields" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"match":{"key":"order_ref","value":"A-1029"},"fields":{"amount":"120.00"}}'{ "updated": 1 }match находит визит по полю, которое у него уже есть, а не по request_id, в
пределах workspace и окружения вашего ключа.404 not_found — пишите
поле вскоре после того, как исход определился.updated считает различные визиты. Значение больше 1 только тогда, когда ваш
match попадает в несколько визитов, — пара ключ/значение, не уникальная для одного
заказа или корзины. match, попадающий больше чем в 100 событий, отклоняется с
400 invalid_request, а не применяется к произвольному подмножеству.server и имеют
приоритет над значением с тем же ключом, записанным браузером.404 not_found, а не пустой 200.Лимиты совпадают с браузерным каналом: 50 полей за вызов, ключи ≤ 40 байт, значения
≤ 500 байт — байт, а не символов, — набор символов ключа A-Za-z0-9_.:-, тело
1 МиБ.
Стирает пользовательские поля — но никогда не сам визит. Используйте его для значений, которые больше не нужно держать привязанными к визиту, например суммы заказа. Уже отправленные вебхуки и уже сделанные выгрузки не меняются.
# by request_id — clears every field on this one visitcurl -s -X POST "https://api.tracio.ai/v2/events/fields/delete" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"request_id":"9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31"}'
# by match — clears "order_ref" from every visit currently holding "A-1029"curl -s -X POST "https://api.tracio.ai/v2/events/fields/delete" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"match":{"key":"order_ref","value":"A-1029"}}'{ "deleted": 1 }request_id и match взаимоисключающие — если отправить оба или ни одного,
будет 400 invalid_request. match использует то же 24-часовое окно и тот же
потолок в 100 событий, что и прикрепление.deleted считает поля, удалённые из визита (по request_id), или визиты, из
которых поле было удалено (по match).404 not_found.Те же списки, которыми ваши операторы управляют в дашборде, доступные из вашего
собственного бэк-офиса. Тип списка — это его действие над совпавшим визитом —
block, manual_review, challenge, allow, — а его режим решает, применяется ли
действие: observe только считает совпадения, enforce применяет его к guidance
(ось list). Списки и их режим создаются в дашборде; Data API читает списки, а также
добавляет и удаляет элементы.
curl -s "https://api.tracio.ai/v2/lists" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX"[ { "id": "7f3a9d2e-4c1b-4e8f-9a6d-1b2c3d4e5f60", "name": "Chargeback accounts", "type": "block", "mode": "enforce", "entries_count": 128 }]# ban an account for 30 dayscurl -s -X POST "https://api.tracio.ai/v2/lists/7f3a9d2e-4c1b-4e8f-9a6d-1b2c3d4e5f60/entries" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"value":"usr_8412","value_type":"linked_id","ttl_minutes":43200}'{ "id": "02b4c8d1-6e7f-4a90-b1c2-d3e4f5a6b7c8", "value": "usr_8412", "value_type": "linked_id", "expires_at": "2026-10-19T12:00:00Z", "propagation": "immediate"}Удалите элемент тем же телом по адресу POST /v2/lists/{list_id}/entries/delete; ответ —
{ "removed": true, "propagation": "immediate" }.
value_type — то, с чем сопоставляется значение: linked_id, visitor_id,
device_id, ip, cidr или country. Значение, которое не разбирается для своего
типа, — это 400 invalid_request.linked_id →
visitor_id → device_id → ip → cidr → country, — а внутри него побеждает
самое строгое действие.ttl_minutes необязателен. Если его опустить, применяется TTL списка по
умолчанию; если нет и его, элемент не истекает никогда (expires_at равно null).
Повторное добавление элемента продлевает его.propagation сообщает, когда изменение вступает в силу: immediate или ttl —
примерно в течение пяти минут.200 с removed: false; 404
зарезервирован за неизвестным списком.Отсутствующее поле означает «нет данных», а не ноль. Поля без значения опускаются
целиком, а не отправляются как 0, "" или null: у совсем нового посетителя нет
match_confidence, у чистого визита нет antidetect_score и suspect_score.
Единственное намеренное исключение — bot.score: оно присутствует всегда, даже когда
равно нулю. Читайте поля защитно.
Payload зависит от вашего тарифа. Доступ к API начинается с Pro, и Pro получает
базовую сессию — идентификаторы, таймстамп, URL, IP, user agent, браузер, ОС,
устройство, гео, сеть, бот, идентификация и решение — вместе с
identification.match_type, identification.match_confidence, bot.score,
bot.type, bot.antidetect_score, gpu, network.proxy_detected, extensions,
fields и account. Business и Enterprise добавляют geo.isp, network.asn, network.real_ip, decision.suspect_score,
identification.drift_score, reasons, behavior, guidance, device_info,
spoofing, os_environment, screen, locale, client_hints и environment.
Поля уровня персоны (person_id, reputation, linked_accounts_count,
linked_visitors_count) зарезервированы за Business и Enterprise и появятся, когда
будет включён слой личности, — сегодня он работает в режиме наблюдения, и эти поля не
отдаются. Отсутствие поля уровня Business на тарифе Pro — не ошибка.
Внутренности уровня сигналов не возвращаются никогда, ни на одном тарифе: имена отдельных сигналов, их веса, пороги за вердиктом, сырые значения сигналов и разложение оценок остаются на нашей стороне. Оценка, которую можно обратной разработкой свести к её входным данным, перестаёт быть защитой.
Любой сбой использует один конверт:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "request_id": "8f14e45fceea167a5a36dedd" }}Этот request_id — не идентификатор визита. Имя делят два разных значения: внутри
payload сессии request_id — это UUID визита, тот же самый, что доставляет вебхук; в
конверте ошибки это 24-символьный идентификатор трассировки, выпускаемый на каждый
HTTP-вызов. Идентификатор трассировки возвращается ещё и в заголовке X-Request-Id
на каждом ответе, успешном или нет. Приложите его при обращении в поддержку — именно
по нему мы находим ваш конкретный вызов.
| HTTP | code | Значение |
|---|---|---|
| 400 | invalid_request | Параметр отсутствует или неверно оформлен |
| 401 | unauthorized | Ключ отсутствует, недействителен, отозван или истёк |
| 402 | upgrade_required | Ваш тариф не включает доступ к API |
| 404 | not_found | Внутри окна хранения ничего не найдено, или данные из другого окружения |
| 405 | method_not_allowed | Маршрут существует, но не для этого метода |
| 429 | rate_limited | Превышены запросы в секунду или дневная квота |
| 500 | internal | Что-то сломалось на нашей стороне |
| 503 | unavailable | Хранилище за API временно недоступно |
Проверки идут в фиксированном порядке — ключ, затем тариф, затем лимиты, — поэтому
запрос с плохим ключом всегда сообщает сначала о ключе, а не о проблеме с квотой.
Данные другого workspace неотличимы от несуществующих: они дают 404, а не 403.
Два случая 401 намеренно читаются по-разному: missing Authorization: Bearer <secret key>
означает, что заголовок вообще не пришёл, а invalid or revoked API key — что он
пришёл и не совпал. 402 несёт Data API requires the Pro plan or higher.
Повторяйте 429 и 503 с растущей паузой; повторять другие 4xx бессмысленно — они
означают, что исправить нужно сам запрос.
Ответы, для которых ключ распознан, несут ваше текущее положение (ответы 401 и
402 его не несут, а Enterprise, у которого нет дневной квоты, не получает
заголовков квоты):
| Заголовок | Значение |
|---|---|
X-RateLimit-Limit | Ваша дневная квота; при отказе по посекундному лимиту — посекундный лимит |
X-RateLimit-Remaining | Сколько вызовов осталось сегодня |
X-RateLimit-Reset | Unix-время сброса — полночь UTC |
Retry-After | Сколько секунд ждать, отправляется только с 429 — ориентируйтесь на него: он верен для обоих лимитов |
| Тариф | Запросов в секунду | Запросов в сутки | Глубина истории |
|---|---|---|---|
| Free | Доступа к API нет | — | 7 дней |
| Pro | 10 | 10 000 | 30 дней |
| Business | 50 | 100 000 | 90 дней |
| Enterprise | 200 | Без ограничений | 365 дней |