Data API (wcześniej opisywane tutaj jako Server API) pozwala Twojemu backendowi czytać dane identyfikacyjne, które TRACIO już zebrało dla Twojego workspace: historię odwiedzającego, pojedyncze sesje oraz liczniki aktywności z krótkiego okna. Kilka tras także zapisuje — rekordy kont, pola niestandardowe i wpisy list — aby Twój backend mógł wzbogacać to, co wie TRACIO.
Uzupełnia Webhooki, a nie zastępuje ich:
| Webhooki | Data API | |
|---|---|---|
| Kierunek | TRACIO wysyła na Twój endpoint | Twój backend pobiera na żądanie |
| Moment | W chwili każdej identyfikacji | W dowolnym momencie, w granicach Twojego okna przechowywania |
| Najlepsze do | Reagowania na zdarzenie | Sprawdzenia danych podczas decyzji, uzupełnień, dochodzeń |
Obie powierzchnie są dostępne od planu Pro wzwyż.
https://api.tracio.ai/v2To inny host niż endpoint przeglądarkowy (edge.tracio.ai) i niż panel
(app.tracio.ai). Wszystkie trzy są osobne: przeglądarka rozmawia z edge Twoim
kluczem publicznym, a Twój backend rozmawia z Data API Twoim kluczem
sekretnym.
Trasy /v1 zostały usunięte. Używaj /v2: trasy są te same, a każdy klucz JSON i
każdy parametr zapytania jest zapisany w snake_case (visitor_id, risk_score,
min_risk_score). Klucze wewnątrz Twoich własnych słowników fields i labels to
Twoje dane i zachowują swoją pierwotną pisownię. JavaScript SDK zachowuje swój
interfejs w camelCase.
Każde żądanie niesie Twój klucz sekretny jako token bearer:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Data API działa wyłącznie w trybie server-to-server. Nagłówki CORS celowo nie są zwracane, więc przeglądarka nie może go wywołać — właśnie to trzyma Twój klucz sekretny poza kodem klienckim. Nigdy nie wysyłaj klucza sekretnego do przeglądarki.
Utwórz go w panelu w sekcji API Keys, wybierając typ secret.
tracio_sk_ plus 43 znaki, łącznie 53. Panel wyświetla
go po kilku pierwszych znakach, żebyś mógł odróżniać klucze od siebie.404, dokładnie tak jak dla danych, które nie istnieją.Rotacja wydaje nowy klucz i utrzymuje stary w działaniu przez 7 dni, więc możesz go wdrożyć bez przestoju. Wdróż nowy klucz, potwierdź, że ruch się przeniósł, i pozwól staremu wygasnąć. Kluczy publicznych się nie rotuje — nie są sekretami i z założenia są widoczne w źródle Twojej strony.
| Metoda | Ścieżka | Zwraca |
|---|---|---|
GET | /v2/visitors/{visitor_id} | Zagregowaną historię jednego odwiedzającego plus jego ostatnią sesję |
GET | /v2/visitors/{visitor_id}/sessions | Stronicowaną listę sesji tego odwiedzającego |
GET | /v2/visitors/{visitor_id}/sessions/latest | Jedną, najnowszą sesję |
GET | /v2/visitors/{visitor_id}/velocity | Liczniki aktywności z krótkiego okna |
GET | /v2/sessions/{request_id} | Jedną sesję po identyfikatorze jej żądania |
POST | /v2/accounts/metadata | Dołącza nazwy wyświetlane i adresy e-mail do Twoich identyfikatorów kont |
POST | /v2/events/fields | Dołącza pola niestandardowe do wizyty po załadowaniu strony |
POST | /v2/events/fields/delete | Usuwa pola niestandardowe z wizyty — samej wizyty nie dotyka |
GET | /v2/lists | Listy Twojego workspace: id, nazwa, typ (akcja), tryb, liczba wpisów |
POST | /v2/lists/{list_id}/entries | Dodaje wartość do listy, opcjonalnie z TTL |
POST | /v2/lists/{list_id}/entries/delete | Usuwa wartość z listy |
GET | /.well-known/webhook-keys | Klucze publiczne podpisu platformy dla webhooków (bez uwierzytelniania) |
Ten sam klucz i host obsługują też API decyzji, w całości w snake_case:
| Metoda | Ścieżka | Przeznaczenie |
|---|---|---|
POST | /v2/transactions | Synchroniczna decyzja APPROVE / REVIEW / DECLINE dotycząca zdarzenia biznesowego — Business i wyżej, z wyjątkiem konwersji partnerskiej (account_register lub deposit z affiliate_id), dostępnej w każdym planie z dostępem do API |
GET | /v2/transactions/{transaction_id} | Ponowny odczyt decyzji wraz z jej etykietą i obowiązującym stanem — Business i wyżej, z wyjątkiem transakcji przypisanej partnerowi |
PUT, DELETE | /v2/transaction-labels | Ustawienie lub usunięcie etykiety wyniku dla maksymalnie 50 transakcji — Business i wyżej |
GET | /v2/transaction-labels/value-set | Dopuszczalne wartości etykiet wraz z ich polaryzacją — Business i wyżej |
POST | /v2/logins/assess | Ocena logowania: allow / challenge / review / deny wraz z powodami |
POST, GET | /v2/accounts/{user_id}/trusted-devices | Oznaczenie urządzenia z wizyty jako zaufanego dla konta po Twojej dodatkowej weryfikacji albo lista zaufanych urządzeń konta |
DELETE | /v2/accounts/{user_id}/trusted-devices/{device_ref} | Cofnięcie zaufania do urządzenia |
GET | /v2/affiliates/{affiliate_id} | Raport jakości partnera z 90 dni z rekomendacją hold / review |
POST, GET | /v2/self-exclusions | Ustawienie lub przedłużenie samowykluczenia konta albo ich lista |
DELETE | /v2/self-exclusions/{user_id} | Wcześniejsze zniesienie samowykluczenia |
Ich wyniki docierają do Ciebie także jako zdarzenia webhooków:
transaction_state_changed, login_risk, trusted_device_enrolled,
trusted_device_revoked i affiliate_threshold_crossed.
Końcowy ukośnik jest akceptowany i ignorowany. Nieznana ścieżka lub zła metoda zwraca tę samą kopertę błędu w JSON co wszystko inne, nigdy stronę w HTML ani czystym tekście.
Każdy odczyt jest ograniczony oknem czasowym, którym sterują dwa opcjonalne parametry zapytania:
| Parametr | Przyjmuje |
|---|---|
from | YYYY-MM-DD albo pełny znacznik czasu RFC 3339 |
to | YYYY-MM-DD albo pełny znacznik czasu RFC 3339 |
to obejmuje cały ten dzień.400 invalid_request i komunikatem
time must be YYYY-MM-DD or RFC3339.meta, więc sprawdzaj
meta.from i meta.to, zamiast zakładać, że Twoje żądanie zostało spełnione
dosłownie.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Odpowiedź niesie zagregowaną historię i osadza w sobie ostatnią sesję, więc typowy przypadek wymaga jednego żądania, a nie dwóch:
{ "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" }}| Pole | Znaczenie |
|---|---|
visits, incognito_visits | Łączna liczba wizyt w oknie i ile z nich odbyło się w oknie prywatnym |
unique_ips, unique_countries | Odrębne adresy i kraje zaobserwowane w oknie |
browsers, os, devices | Odrębne środowiska, w których pojawiał się ten odwiedzający |
risk.max_risk_score | Najwyższy wskaźnik ryzyka zanotowany w oknie, 0..100 |
risk.last_decision | Decyzja zanotowana dla najnowszej wizyty |
risk.avg_bot_score, risk.bot_sessions | Średni wskaźnik bota i liczba sesji botów — Business i wyżej |
network.*_seen | Czy u tego odwiedzającego kiedykolwiek widziano VPN, proxy, węzeł wyjściowy Tor lub adres centrum danych |
network.proxy_detected_seen | Czy co najmniej jedna wizyta w oknie wyszła przez proxy lub VPN stojące przed przeglądarką — zob. network.proxy_detected w sekcji Fakty o urządzeniu |
network.last_isp | Najnowszy ISP — Business i wyżej |
network.last_real_ip | Najnowszy adres zaobserwowany za proxy lub VPN (address, country, isp) — Business i wyżej; brak, gdy żadna wizyta go nie przyniosła |
last_session | Pełny obiekt sesji dla najnowszej wizyty |
meta | Plan, jego okres przechowywania w dniach oraz faktycznie zastosowane okno |
Odwiedzający, dla którego nie ma danych wewnątrz okna przechowywania, zwraca
404 not_found z komunikatem visitor not found in the retention window — to nie
błąd w Twojej integracji, lecz znak, że odwiedzający jest nowy albo wypadł poza okres
przechowywania.
Sesja niesie dwa werdykty i odpowiadają one na różne pytania — czy klient był zautomatyzowany oraz do jakiego ogólnego wniosku doszedł silnik ryzyka:
| Pole | Wartości |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Obecne, gdy bot.result ma wartość bot: albo konkretne narzędzie (playwright, puppeteer, selenium, jsdom, claude_computer_use…), albo zweryfikowany agent AI (ai_agent:<vendor>), albo rodzina, gdy narzędzie nie zostało nazwane — automation, headless, antidetect, extension, privacy_browser, impersonation, other |
decision.action | real, fake, suspicious |
bot.score i decision.risk_score mieszczą się oba w skali 0..100. W planach
Business i wyżej guidance zamienia je w rekomendacje dla poszczególnych scenariuszy
na drabinie allow → challenge → review → deny — co oznacza każdy szczebel, opisuje
Guidance. Wartości i skale są zgodne z
treścią webhooka v3 dla tej samej wizyty, a guidance jest liczone według tych samych
reguł.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&min_risk_score=50"| Parametr | Domyślnie | Uwagi |
|---|---|---|
limit | 50 | Ograniczony do 500; większa wartość jest przycinana, nie odrzucana |
from, to | Okres przechowywania planu | Wspólne okno czasowe opisane wyżej |
cursor | — | Nieprzezroczysty kursor paginacji z poprzedniej strony |
bot_result | — | Zostaw tylko sesje z tym werdyktem bota |
min_risk_score | — | Zostaw tylko sesje o wskaźniku ryzyka nie niższym niż ten, 0..100 |
Sesje wracają od najnowszych:
{ "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" }}Stronicowanie opiera się na kursorach. Nie ma parametru page ani offset: przekaż
otrzymany next_cursor z powrotem jako cursor i kontynuuj, dopóki has_more jest
prawdziwe. Ostatnia strona może w ogóle nie zawierać next_cursor, więc nie rób tego
pola wymaganym.
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}Traktuj kursor jako nieprzezroczysty — jego zawartość jest szczegółem implementacyjnym
i może się zmienić. Kursor, który został zmodyfikowany, jest odrzucany kodem
400 invalid_request i komunikatem malformed cursor.
Obok przeglądarki i systemu operacyjnego wziętych z User-Agent sesja niesie to, co przeglądarka odwiedzającego zgłasza o maszynie, oczyszczone po naszej stronie. Każde pole jest nieobecne, gdy wizyta nie przyniosła takich danych, więc każde traktuj jako opcjonalne.
| Pole | Znaczenie |
|---|---|
gpu | Model karty graficznej zgłaszany przez przeglądarkę (WebGL), znormalizowany do czytelnej nazwy — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer oznacza brak prawdziwego GPU (maszyna wirtualna lub środowisko headless); Safari zgłasza Apple GPU |
network.proxy_detected | Ruch HTTP wizyty i jej surowe ścieżki sieciowe wychodzą przez różne sieci — przed przeglądarką stoi proxy lub VPN; dwa adresy tego samego dostawcy (NAT operatora, drugie wyjście tego samego VPN) nie liczą się; flaga nie jest ustawiana, gdy adresów nie da się porównać (iCloud Private Relay, IPv4 obok IPv6) |
network.real_ip.address, .country, .isp | Publiczny adres zaobserwowany na surowej ścieżce sieciowej, czyli adres za proxy lub VPN, wraz z jego krajem i ISP — Business i wyżej; brak, gdy takiego adresu nie zaobserwowano (country i isp są nieobecne, gdy nie dało się ich ustalić) |
device_info.device_id, .cross_browser, .confidence, .linked_browsers | Obecne, gdy udało się ustalić tożsamość urządzenia: stabilny identyfikator fizycznego urządzenia wspólny dla przeglądarek na nim, czy ta wizyta przyszła przez inną przeglądarkę niż poprzednio, pewność tego dopasowania oraz ilu odrębnych odwiedzających (przeglądarek) dzieli urządzenie — więcej niż jeden oznacza jedną maszynę pod kilkoma tożsamościami przeglądarek — Business i wyżej |
os_environment | Środowisko pulpitu zmierzone na maszynie z Linuksem (Mint 22+, Ubuntu 25.04+, Ubuntu, GNOME, KDE) — Business i wyżej; brak, gdy nie ustalono |
spoofing | Co wizyta twierdziła wobec tego, co zmierzyły niezależne kontrole (claimed, real, spoofed_axes spośród os, gpu, screen, network, browser; anonymous_browser z nazwami produktów) — Business i wyżej; obecne tylko wtedy, gdy podmianę wykryto |
screen.width, .height, .color_depth, .pixel_ratio | Rozdzielczość ekranu, głębia koloru i device pixel ratio zgłaszane przez przeglądarkę — Business i wyżej |
locale.languages, locale.timezone | Własne preferowane języki przeglądarki i jej strefa czasowa — w odróżnieniu od geo.timezone, wyprowadzanej z adresu IP; rozbieżność między nimi to częsta oznaka podrobionej lokalizacji — Business i wyżej |
client_hints.architecture, .bitness, .model, .device_name, .platform_version | User-Agent Client Hints: architektura i bitowość procesora, kod modelu urządzenia (Android, np. SM-A556B) wraz z jego nazwą handlową z listy urządzeń Google Play (device_name, np. Samsung Galaxy A55 5G) i dokładna wersja platformy; tylko przeglądarki oparte na Chromium — Business i wyżej |
environment.virtual_machine, environment.hypervisor | Obecne tylko wtedy, gdy karta graficzna sama przedstawiła się jako wirtualna; hypervisor to słownik zamknięty (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Brak bloku oznacza, że nie ma takich przesłanek — Business i wyżej |
extensions wymienia rozszerzenia przeglądarki wykryte podczas wizyty — Pro i wyżej.
Każdy wpis jest obiektem:
| Pole | Znaczenie |
|---|---|
slug | Stabilny, maszynowy identyfikator rozszerzenia — ta sama wartość, którą dostarcza webhook |
name | Nazwa czytelna dla człowieka |
category | Zgrubna klasa — adblock, privacy, automation, wallet, vpn, devtools, other i tak dalej |
risky | true dla rozszerzeń powiązanych z automatyzacją, podmianą lub kradzieżą danych uwierzytelniających |
store_url | Link do wpisu rozszerzenia w sklepie, gdy jest znany |
Znalezisko jest raportowane dopiero po przejściu naszych kontroli wiarygodności — środowisko, które na każdą sondę odpowiada „zainstalowane”, albo partia dłuższa niż dwanaście nazw, jest odrzucane jako niewiarygodne. Pusta lub brakująca lista oznacza więc „nic, co udało nam się potwierdzić”, a nie „nie zainstalowano żadnych rozszerzeń”. Czytaj ją jako dowód, a nie jako inwentarz.
fields zawiera niestandardowe pary klucz–wartość, które Twoja integracja dołączyła
do wizyty — numer zamówienia, nazwę planu, wynik płatności. Klucze i wartości są
zawsze ciągami znaków: dołączona liczba lub wartość logiczna wraca w postaci
tekstowej. Podobnie jak reszta sesji, pole jest dostępne w każdym planie z dostępem do
API (Pro i wyżej) i nie występuje, gdy wizyta nie ma pól.
fields jest odczytywane na bieżąco przy każdym żądaniu, a nie kopiowane w chwili
wizyty. Pola dołączone po wizycie (zob. Dołączanie pól po wizycie)
także się tu pojawiają, więc dla tej samej wizyty fields może zawierać więcej, niż
dostarczył webhook.
Nie umieszczaj w polach niestandardowych danych osobowych, takich jak imiona i nazwiska, adresy e-mail czy numery telefonów — zamiast nich dołączaj identyfikator, który rozpoznają Twoje własne systemy, a to, kim jest właściciel konta, przekazuj jako rekord konta (poniżej).
account to Twój rekord konta stojącego za wizytą — to, co ostatnio wysłałeś dla tego
account_id przez POST /v2/accounts/metadata:
{ "account": { "id": "user-42", "email": "ann@example.com", "display_name": "Ann Lee" }}| Pole | Opis |
|---|---|
id | Twój identyfikator konta — ta sama wartość co account_id |
email | Adres e-mail, który wysłałeś dla konta; brak, gdy jest pusty |
display_name | Nazwa wyświetlana, którą wysłałeś dla konta; brak, gdy jest pusta |
Rekord jest odczytywany na bieżąco przy każdym żądaniu, więc zmianę nazwy widać także na starych wizytach: blok opisuje konto takim, jakie jest teraz, a nie takim, jakie było w chwili wizyty. Jest dostępny w każdym planie z dostępem do API (Pro i wyżej). Etykiety wysłane wraz z rekordem nie są zwracane.
Bloku nie ma, gdy wizyta nie ma powiązanego konta, gdy nie ma dla niego rekordu (albo wyczyściłeś go, wysyłając puste wartości), oraz — rzadko — gdy rekordu nie udało się odczytać: sesja i tak jest zwracana w całości, zamiast przerywać żądanie, więc traktuj brakujący blok jako „nieznane”, a nie „to konto nie ma adresu e-mail”. Webhooki nie niosą tego bloku.
Ta najnowsza:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Zwracany jest goły obiekt sesji — nie tablica i nie opakowanie w kopertę.
Odwiedzający bez sesji w oknie zwraca 404 not_found z komunikatem
no sessions for this visitor in the retention window.
Albo po request_id, identyfikatorze, który pojawia się także w payloadzie webhooka:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitor_id=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitor_id jest tutaj opcjonalny, ale przekazanie go, gdy go znasz, wyraźnie
przyspiesza wyszukiwanie.
identification.drift_score — wygładzony wskaźnik zachowania, na którym oparto
werdykt o przejęciu konta, Business i wyżej — jest zwracany tylko przez ten odczyt
pojedynczej sesji. Dryf zachowania jest mierzony w trakcie wizyty, po początkowej
identyfikacji, więc trasy listy i najnowszej sesji go pomijają. Wizyta, której
zachowania nigdy nie oceniono, nie ma drift_score. Ta sama wartość dociera do
Ciebie przez webhook account_takeover.
Webhook może dostarczyć wizytę więcej niż raz: najpierw primary, potem late lub
correction, w miarę jak docierają wolniejsze sygnały i dowody behawioralne — a
późniejsza faza może odwrócić wcześniejszy werdykt. Data API odpowiada na podstawie
rekordu primary. Późniejsze fazy nie są do niego scalane, więc dla skorygowanej
wizyty ostatni webhook jest nowszą odpowiedzią. Jeśli działasz kilka minut po wizycie,
przedkładaj fazę otrzymaną jako ostatnią nad świeży odczyt tutaj.
Jedno działa w drugą stronę: gdy detektor przejęcia konta zadziałał dla wizyty,
guidance tutaj to odzwierciedla, podczas gdy treści webhooka identification tej
samej wizyty tej osi nie niosą — trafia ona w osobnym doręczeniu account_takeover.
Velocity odpowiada na pytanie „ile ten odwiedzający ostatnio narobił” — tak właśnie wygląda upychanie danych logowania, testowanie kart i masowe rejestracje.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window przyjmuje 1h, 24h lub 7d, a domyślnie ma 24h. Każda inna wartość jest
odrzucana kodem 400 invalid_request i komunikatem
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 liczy odrębne wartości linkedId, które wysłałeś dla tego
urządzenia — zobacz Łączenie kont. bot_events jest dostępne
od planu Business wzwyż; w planie Pro klucza nie ma.
TRACIO zna Twoje konta tylko po identyfikatorze, który Twoja strona przekazuje jako
linkedId — nieprzezroczystym ciągu w rodzaju usr_8412. Wyślij czytelne etykiety
raz, a każdy ekran panelu, który pokazuje to konto, pokaże nazwę lub adres e-mail
zamiast surowego identyfikatora, a obiekt sesji zwróci je w swoim bloku
account. Nic tutaj nie wpływa na wykrywanie ani ocenę — to warstwa
prezentacji.
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. Adres e-mail lub imię
wysłane tutaj żyją tylko w rekordzie konta Twojego workspace: nie są przechowywane
dla każdej wizyty osobno, nie trafiają do strony, a jedno wysłanie aktualizuje każdą
wizytę tego konta.400 invalid_request, a jeden błędny wpis
odrzuca całą partię — błąd wskazuje jego pozycję. Powtórzone identyfikatory kont w
jednej partii są w porządku: wygrywa ostatni.Limity: 1000 kont na żądanie, treść 1 MiB, account_id ≤ 256 znaków,
email ≤ 320, display_name ≤ 256, najwyżej 16 etykiet z kluczami ≤ 64 i wartościami
≤ 256 znaków.
Przeglądarka może wysłać tylko to, co strona wie w chwili renderowania — numer zamówienia, nigdy wynik weryfikacji płatności ani nic innego, co rozstrzyga się później. Ta trasa wypełnia tę lukę: znajdź wizytę po polu, które już ma, i dołącz do niej kolejne pola po stronie serwera.
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 znajduje wizytę po polu, które już ma, a nie po request_id, w obrębie
workspace i środowiska Twojego klucza.404 not_found — zapisz
pole wkrótce po rozstrzygnięciu wyniku.updated liczy odrębne wizyty. Przekracza 1 tylko wtedy, gdy Twój match
trafia w więcej niż jedną wizytę — para klucz/wartość, która nie jest unikalna dla
jednego zamówienia czy koszyka. match, który trafia w więcej niż 100 zdarzeń, jest
odrzucany kodem 400 invalid_request, zamiast być zastosowany do dowolnego
podzbioru.server i mają
pierwszeństwo przed wartością o tym samym kluczu zapisaną przez przeglądarkę.404 not_found, a nie pusty 200.Limity są takie same jak w kanale przeglądarkowym: 50 pól na wywołanie, klucze ≤ 40
bajtów, wartości ≤ 500 bajtów — bajtów, nie znaków — zestaw znaków klucza
A-Za-z0-9_.:-, treść 1 MiB.
Usuwa pola niestandardowe — nigdy samą wizytę. Używaj tego dla wartości, których nie potrzebujesz już dołączonych do wizyty, takich jak kwota zamówienia. Webhooki już wysłane i eksporty już wykonane nie ulegają zmianie.
# 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 i match wzajemnie się wykluczają — wysłanie obu albo żadnego to
400 invalid_request. match używa tego samego 24-godzinnego okna i tego samego
limitu 100 zdarzeń co dołączanie.deleted liczy pola usunięte z wizyty (po request_id) albo wizyty, z których
usunięto pole (po match).404 not_found.Te same listy, którymi Twoi operatorzy zarządzają w panelu, dostępne z Twojego
własnego back office. Typ listy to jej akcja wobec pasującej wizyty — block,
manual_review, challenge, allow — a jej tryb decyduje, czy akcja jest
stosowana: observe tylko liczy dopasowania, enforce stosuje ją w guidance (oś
list). Listy i ich tryb tworzy się w panelu; Data API odczytuje listy oraz dodaje i
usuwa wpisy.
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"}Usuń wpis tą samą treścią pod POST /v2/lists/{list_id}/entries/delete; odpowiedź to
{ "removed": true, "propagation": "immediate" }.
value_type to to, z czym porównywana jest wartość: linked_id, visitor_id,
device_id, ip, cidr lub country. Wartość, której nie da się sparsować dla jej
typu, to 400 invalid_request.linked_id →
visitor_id → device_id → ip → cidr → country — a w jego obrębie wygrywa
najsurowsza akcja.ttl_minutes jest opcjonalny. Gdy go pominiesz, obowiązuje domyślny TTL listy;
gdy i tego brak, wpis nigdy nie wygasa (expires_at ma wartość null). Ponowne
dodanie wpisu odnawia go.propagation mówi, kiedy zmiana zaczyna obowiązywać: immediate albo ttl — w
ciągu około pięciu minut.200 z removed: false; 404 jest
zarezerwowane dla nieznanej listy.Brakujące pole oznacza „brak danych”, nigdy zero. Pola bez wartości są pomijane w
całości, zamiast być wysyłane jako 0, "" czy null: zupełnie nowy odwiedzający
nie ma match_confidence, czysta wizyta nie ma antidetect_score ani
suspect_score. Jedynym celowym wyjątkiem jest bot.score, które występuje zawsze,
nawet gdy wynosi zero. Czytaj pola ostrożnie.
Payload zależy od Twojego planu. Dostęp do API zaczyna się od Pro, a Pro dostaje
bazową sesję — identyfikatory, znacznik czasu, URL, IP, user agent, przeglądarkę,
system operacyjny, urządzenie, geo, sieć, bota, identyfikację i decyzję — wraz z
identification.match_type, identification.match_confidence, bot.score,
bot.type, bot.antidetect_score, gpu, network.proxy_detected, extensions,
fields i account. Business i Enterprise dokładają 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 i environment. Pola na poziomie osoby (person_id,
reputation, linked_accounts_count, linked_visitors_count) są zarezerwowane dla
Business i Enterprise i pojawią się, gdy zostanie włączona warstwa osób — dziś działa
ona w trybie obserwacji, a te pola nie są dostarczane. Brak pola z planu Business w
planie Pro nie jest błędem.
Wewnętrzne szczegóły na poziomie sygnałów nie są zwracane nigdy, w żadnym planie: nazwy poszczególnych sygnałów, ich wagi, progi stojące za werdyktem, surowe wartości sygnałów i rozbicia wskaźników zostają po naszej stronie. Wskaźnik, który da się odtworzyć wstecz do swoich danych wejściowych, przestaje być użyteczny jako zabezpieczenie.
Każde niepowodzenie używa jednej koperty:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "request_id": "8f14e45fceea167a5a36dedd" }}Ten request_id nie jest identyfikatorem wizyty. Nazwę dzielą dwie różne wartości:
wewnątrz payloadu sesji request_id to UUID wizyty, ten sam, który dostarcza webhook;
wewnątrz koperty błędu jest to 24-znakowy identyfikator śledzenia, wybijany dla
każdego wywołania HTTP. Identyfikator śledzenia wraca również w nagłówku
X-Request-Id przy każdej odpowiedzi, udanej czy nie. Dołącz go, kontaktując się ze
wsparciem — właśnie po nim odnajdujemy Twoje konkretne wywołanie.
| HTTP | code | Znaczenie |
|---|---|---|
| 400 | invalid_request | Brakuje parametru albo jest źle sformułowany |
| 401 | unauthorized | Klucza brak, jest nieprawidłowy, odwołany lub wygasły |
| 402 | upgrade_required | Twój plan nie obejmuje dostępu do API |
| 404 | not_found | Wewnątrz okna przechowywania nic nie pasowało albo dane należą do innego środowiska |
| 405 | method_not_allowed | Trasa istnieje, ale nie dla tej metody |
| 429 | rate_limited | Przekroczono liczbę zapytań na sekundę albo kwotę dzienną |
| 500 | internal | Coś zawiodło po naszej stronie |
| 503 | unavailable | Magazyn danych jest chwilowo nieosiągalny |
Kontrole biegną w ustalonej kolejności — klucz, potem plan, potem limity — więc
żądanie ze złym kluczem zawsze zgłasza najpierw klucz, nigdy problem z kwotą. Dane z
innego workspace są nie do odróżnienia od danych, które nie istnieją: dają 404, a
nie 403.
Dwa przypadki 401 czyta się celowo inaczej: missing Authorization: Bearer <secret key>
oznacza, że nagłówek w ogóle nie dotarł, a invalid or revoked API key — że dotarł i
nie pasował. 402 niesie Data API requires the Pro plan or higher.
Ponawiaj 429 i 503 z rosnącą przerwą; ponawianie innych 4xx nie ma sensu —
oznaczają one, że poprawić trzeba samo żądanie.
Odpowiedzi, dla których klucz został rozpoznany, niosą Twój bieżący stan (odpowiedzi
401 i 402 go nie niosą, a Enterprise, bez kwoty dziennej, nie dostaje nagłówków
kwoty):
| Nagłówek | Znaczenie |
|---|---|
X-RateLimit-Limit | Twoja kwota dzienna; przy odmowie z powodu limitu na sekundę — limit na sekundę |
X-RateLimit-Remaining | Wywołania pozostałe na dziś |
X-RateLimit-Reset | Czas uniksowy resetu — północ UTC |
Retry-After | Sekundy oczekiwania, wysyłane tylko przy 429 — kieruj się nim: jest poprawny dla obu limitów |
| Plan | Zapytań na sekundę | Zapytań dziennie | Głębokość historii |
|---|---|---|---|
| Free | Brak dostępu do API | — | 7 dni |
| Pro | 10 | 10 000 | 30 dni |
| Business | 50 | 100 000 | 90 dni |
| Enterprise | 200 | Bez limitu | 365 dni |