Server API позволяет вашему бэкенду читать данные идентификации, которые TRACIO уже собрал для вашего workspace: историю посетителя, отдельные сессии и счётчики активности за короткое окно.
Он дополняет Webhooks, а не заменяет их:
| Webhooks | Server API | |
|---|---|---|
| Направление | TRACIO отправляет на ваш эндпоинт | Ваш бэкенд запрашивает сам, когда нужно |
| Момент | В момент каждой идентификации | В любое время, в пределах вашего окна хранения |
| Лучше для | Реакции на событие | Поиска данных в момент решения, дозагрузок, разбора инцидентов |
Обе поверхности доступны с тарифа Pro и выше.
https://api.tracio.ai/v1Это отдельный хост — не тот, что у браузерного эндпоинта (edge.tracio.ai), и не
тот, что у дашборда (app.tracio.ai). Все три разные: браузер общается с edge вашим
публичным ключом, ваш бэкенд общается с Server API вашим секретным ключом.
Каждый запрос несёт ваш секретный ключ как bearer-токен:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Server API работает только в режиме server-to-server. Заголовки CORS намеренно не возвращаются, поэтому браузер не может его вызвать — именно это удерживает ваш секретный ключ вне клиентского кода. Никогда не отдавайте секретный ключ в браузер.
Создайте его в дашборде, в разделе API Keys, выбрав тип secret.
tracio_sk_ плюс 43 символа, всего 53. Дашборд
показывает его по первым нескольким символам, чтобы вы могли отличать ключи
друг от друга.Ротация выпускает новый ключ и оставляет старый рабочим ещё 7 дней, так что вы можете выкатить замену без простоя. Задеплойте новый ключ, убедитесь, что трафик перешёл на него, и дайте старому истечь. Публичные ключи не ротируются — они не секреты и по замыслу видны в исходном коде вашей страницы.
Все маршруты — GET. Операций записи в Server API нет: он читает данные, а ваша
конфигурация живёт в дашборде.
| Метод | Путь | Возвращает |
|---|---|---|
GET | /v1/visitors/{visitorId} | Агрегированную историю одного посетителя плюс его последнюю сессию |
GET | /v1/visitors/{visitorId}/sessions | Постраничный список сессий этого посетителя |
GET | /v1/visitors/{visitorId}/sessions/latest | Одну самую свежую сессию |
GET | /v1/visitors/{visitorId}/velocity | Счётчики активности за короткое окно |
GET | /v1/sessions/{requestId} | Одну сессию по идентификатору её запроса |
GET | /.well-known/webhook-keys | Публичные ключи для подписи платформы у вебхуков (без аутентификации) |
Завершающий слеш принимается и игнорируется. Неизвестный путь или неверный метод возвращают тот же 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/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Ответ несёт агрегированную историю и включает в себя последнюю сессию, поэтому в типичном случае хватает одного запроса, а не двух:
{ "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "firstSeenAt": "2026-05-02T10:11:12Z", "lastSeenAt": "2026-07-25T08:00:00Z", "visits": 42, "incognitoVisits": 3, "uniqueIps": 5, "uniqueCountries": 2, "browsers": ["Chrome"], "os": ["macOS"], "devices": ["desktop"], "risk": { "maxRiskScore": 63, "avgBotScore": 12.5, "botSessions": 7, "lastDecision": "real" }, "network": { "vpnSeen": false, "proxySeen": false, "torSeen": false, "datacenterSeen": true, "lastIsp": "Deutsche Telekom" }, "lastSession": { "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "accountId": "user_8842", "timestamp": "2026-07-25T08:00:00Z", "tag": "checkout", "url": "https://shop.example.com/checkout", "ip": "203.0.113.42", "userAgent": "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", "geo": { "country": "DE", "city": "Berlin", "timezone": "Europe/Berlin", "isp": "Deutsche Telekom" }, "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "asn": 3320 }, "bot": { "result": "human", "score": 4.5, "antidetectScore": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "matchType": "exact", "matchConfidence": 0.99 }, "decision": { "action": "real", "riskScore": 12 } }, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| Поле | Значение |
|---|---|
visits, incognitoVisits | Всего визитов в окне и сколько из них были в приватном окне |
uniqueIps, uniqueCountries | Различные адреса и страны, встреченные в окне |
browsers, os, devices | Различные окружения, в которых появлялся этот посетитель |
risk.maxRiskScore | Наивысшая оценка риска, зафиксированная в окне, 0..100 |
risk.lastDecision | Решение, зафиксированное для самого свежего визита |
risk.avgBotScore, risk.botSessions | Средняя оценка бота и число ботовых сессий — Business и выше |
network.*Seen | Встречались ли когда-либо у этого посетителя VPN, прокси, выходной узел Tor или адрес ЦОД |
network.lastIsp | Самый свежий ISP — Business и выше |
lastSession | Полный объект сессии для самого свежего визита |
meta | Тариф, его срок хранения в днях и фактически применённое окно |
Посетитель, по которому нет данных внутри окна хранения, отдаёт 404 not_found с
сообщением visitor not found in the retention window — это не ошибка вашей
интеграции, а признак того, что посетитель новый или уже вышел за срок хранения.
Сессия несёт два вердикта, и они отвечают на разные вопросы — был ли клиент автоматизирован и к какому общему выводу пришёл движок риска:
| Поле | Значения |
|---|---|
bot.result | human, bot, uncertain |
decision.action | real, fake, suspicious |
bot.score и decision.riskScore оба идут в шкале 0..100. На Business и выше
guidance превращает их в рекомендации по каждому сценарию на лестнице
allow → challenge → review → deny — что означает каждая ступень, см. в разделе
Guidance.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Параметр | По умолчанию | Примечания |
|---|---|---|
limit | 50 | Ограничен сверху 500; большее значение подрезается, а не отклоняется |
from, to | Срок хранения тарифа | Общее временное окно, описанное выше |
cursor | — | Непрозрачный курсор пагинации с предыдущей страницы |
botResult | — | Оставить только сессии с этим вердиктом бота |
minRiskScore | — | Оставить только сессии с оценкой риска не ниже этой, 0..100 |
Сессии возвращаются начиная с самых свежих:
{ "items": [ { "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "timestamp": "2026-07-25T08:00:00Z" } ], "nextCursor": "MTcyMTg5NDQwMDAwMDphYmMxMjM", "hasMore": true, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}Постраничная выборка построена на курсорах. Параметров page или offset нет:
передайте полученный nextCursor обратно как cursor и продолжайте, пока hasMore
равно true.
async function allSessions(visitorId: string, secretKey: string) { const sessions = [] let cursor: string | undefined
do { const url = new URL(`https://api.tracio.ai/v1/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(`Server API: ${res.status}`)
const page = await res.json() sessions.push(...page.items) cursor = page.nextCursor } while (cursor)
return sessions}Считайте курсор непрозрачным — его содержимое является деталью реализации и может
измениться. Отредактированный курсор отклоняется с 400 invalid_request и сообщением
malformed cursor.
Самая свежая:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Возвращается голый объект сессии — не массив и не обёрнутый в конверт. Посетитель без
сессий в окне отдаёт 404 not_found с сообщением
no sessions for this visitor in the retention window.
Либо по requestId — идентификатору, который встречается и в payload вебхука:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId здесь необязателен, но если вы его знаете, передача заметно ускоряет
поиск.
Velocity отвечает на вопрос «сколько этот посетитель успел наделать за последнее время» — именно так выглядят перебор учётных данных, тестирование карт и массовые регистрации.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/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, "uniqueIps": 9, "uniqueCountries": 3, "uniqueAccounts": 12, "botEvents": 4, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-07-25T11:00:00Z", "to": "2026-07-25T12:00:00Z" }}uniqueAccounts считает различные значения linkedId, которые вы отправили для
этого устройства, — см. Связывание аккаунтов. botEvents
доступно на Business и выше.
Отсутствующее поле означает «нет данных», а не ноль. Поля без значения опускаются
целиком, а не отправляются как 0, "" или null: у совсем нового посетителя нет
matchConfidence, у чистого визита нет antidetectScore и suspectScore.
Единственное намеренное исключение — bot.score: оно присутствует всегда, даже когда
равно нулю. Читайте поля защитно.
Payload зависит от вашего тарифа. Любой тариф с доступом к API получает базовую
сессию — идентификаторы, таймстамп, URL, IP, user agent, браузер, ОС, устройство,
гео, сеть, бот, идентификация и решение. Pro добавляет identification.matchType,
identification.matchConfidence и bot.antidetectScore. Business и Enterprise
добавляют geo.isp, network.asn, decision.suspectScore,
identification.driftScore, reasons, behavior, guidance, deviceInfo и поля
уровня персоны (personId, reputation, linkedAccountsCount,
linkedVisitorsCount). Отсутствие поля уровня Business на тарифе Pro — не ошибка.
Внутренности уровня сигналов не возвращаются никогда, ни на одном тарифе: имена отдельных сигналов, их веса, пороги за вердиктом, сырые значения сигналов и разложение оценок остаются на нашей стороне. Оценка, которую можно обратной разработкой свести к её входным данным, перестаёт быть защитой.
Любой сбой использует один конверт:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}Этот requestId — не идентификатор визита. Имя делят два разных значения: внутри
payload сессии requestId — это 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 временно недоступно |
Проверки идут в фиксированном порядке — ключ, затем тариф, затем лимиты, — поэтому запрос с плохим ключом всегда сообщает сначала о ключе, а не о проблеме с квотой.
Два случая 401 намеренно читаются по-разному: missing Authorization: Bearer <secret key>
означает, что заголовок вообще не пришёл, а invalid or revoked API key — что он
пришёл и не совпал. 402 несёт Data API requires the Pro plan or higher.
Каждый аутентифицированный ответ несёт ваше текущее положение:
| Заголовок | Значение |
|---|---|
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 дней |