Server API permite backend-ului dumneavoastră să citească datele de identificare pe care TRACIO le-a colectat deja pentru workspace-ul dumneavoastră: istoricul unui vizitator, sesiunile individuale și contoarele de velocity pe ferestre scurte.
Completează Webhooks, fără să le înlocuiască:
| Webhooks | Server API | |
|---|---|---|
| Direcție | TRACIO trimite către endpoint-ul dumneavoastră | Backend-ul dumneavoastră interoghează la cerere |
| Moment | Pe măsură ce se produce fiecare identificare | Oricând, în cadrul ferestrei dumneavoastră de retenție |
| Potrivit pentru | Reacția la un eveniment | Consultarea datelor în timpul unei decizii, backfill-uri, investigații |
Ambele suprafețe sunt disponibile începând cu planul Pro.
https://api.tracio.ai/v1Acesta este un host diferit de endpoint-ul din browser (edge.tracio.ai) și de
dashboard (app.tracio.ai). Toate trei sunt separate: browserul vorbește cu edge-ul
folosind cheia dumneavoastră publică, iar backend-ul dumneavoastră vorbește cu
Server API folosind cheia secretă.
Fiecare cerere poartă cheia dumneavoastră secretă ca bearer token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Server API este exclusiv server-to-server. Antetele CORS nu sunt returnate în mod deliberat, așa că un browser nu o poate apela — tocmai asta ține cheia dumneavoastră secretă în afara codului de pe client. Nu trimiteți niciodată cheia secretă în browser.
Creați-o în dashboard, la API Keys, alegând tipul secret.
tracio_sk_ urmat de 43 de caractere, 53 în total.
Dashboard-ul o listează după primele câteva caractere, ca să puteți deosebi cheile
între ele.Rotirea emite o cheie nouă și o menține pe cea veche funcțională timp de 7 zile, ca să o puteți desfășura fără întreruperi. Desfășurați cheia nouă, confirmați că traficul s-a mutat și lăsați-o pe cea veche să expire. Cheile publice nu se pot roti — nu sunt secrete și sunt vizibile prin design în sursa paginii dumneavoastră.
Fiecare rută este un GET. În Server API nu există operațiuni de scriere: ea citește
date, iar configurația dumneavoastră trăiește în dashboard.
| Metodă | Cale | Returnează |
|---|---|---|
GET | /v1/visitors/{visitorId} | Istoric agregat pentru un vizitator, plus ultima lui sesiune |
GET | /v1/visitors/{visitorId}/sessions | Listă paginată a sesiunilor acelui vizitator |
GET | /v1/visitors/{visitorId}/sessions/latest | Singura cea mai recentă sesiune |
GET | /v1/visitors/{visitorId}/velocity | Contoare de activitate pe o fereastră scurtă |
GET | /v1/sessions/{requestId} | O sesiune, după identificatorul cererii ei |
GET | /.well-known/webhook-keys | Chei publice pentru semnătura de platformă a webhook-urilor (fără autentificare) |
O bară finală este acceptată și ignorată. O cale necunoscută sau o metodă greșită returnează același plic JSON de eroare ca tot restul, niciodată o pagină HTML sau de text simplu.
Fiecare citire este delimitată de o fereastră temporală, controlată prin doi parametri de query opționali:
| Parametru | Acceptă |
|---|---|
from | YYYY-MM-DD sau un timestamp RFC 3339 complet |
to | YYYY-MM-DD sau un timestamp RFC 3339 complet |
to include întreaga zi respectivă.400 invalid_request și mesajul
time must be YYYY-MM-DD or RFC3339.meta, așa că verificați meta.from și meta.to în loc să
presupuneți că cererea dumneavoastră a fost onorată întocmai.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Răspunsul poartă istoricul agregat și include ultima sesiune, așa că în cazul obișnuit este nevoie de o singură cerere, nu de două:
{ "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" }}| Câmp | Semnificație |
|---|---|
visits, incognitoVisits | Vizite totale în fereastră și câte dintre ele au fost într-o fereastră privată |
uniqueIps, uniqueCountries | Adrese și țări distincte văzute în fereastră |
browsers, os, devices | Mediile distincte în care a apărut acest vizitator |
risk.maxRiskScore | Cel mai mare scor de risc înregistrat în fereastră, 0..100 |
risk.lastDecision | Decizia înregistrată pentru cea mai recentă vizită |
risk.avgBotScore, risk.botSessions | Media scorului de bot și numărul sesiunilor de bot — Business și peste |
network.*Seen | Dacă pentru acest vizitator au fost văzute vreodată VPN, proxy, nod de ieșire Tor sau adresă de datacenter |
network.lastIsp | Cel mai recent ISP — Business și peste |
lastSession | Obiectul sesiune complet pentru cea mai recentă vizită |
meta | Planul, retenția lui în zile și fereastra aplicată efectiv |
Un vizitator fără date în fereastra de retenție returnează 404 not_found cu mesajul
visitor not found in the retention window — nu este o eroare în integrarea
dumneavoastră, ci înseamnă că vizitatorul este nou sau a ieșit din fereastră odată cu
trecerea timpului.
Sesiunea poartă două verdicte, iar ele răspund la întrebări diferite — dacă clientul a fost automatizat și ce a concluzionat în ansamblu motorul de risc:
| Câmp | Valori |
|---|---|
bot.result | human, bot, uncertain |
decision.action | real, fake, suspicious |
bot.score și decision.riskScore merg amândouă pe 0..100. Pe Business și peste,
guidance le transformă în recomandări per scenariu, pe scara
allow → challenge → review → deny — vedeți
Guidance pentru ce înseamnă fiecare
treaptă.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Parametru | Implicit | Note |
|---|---|---|
limit | 50 | Plafonat la 500; o valoare mai mare este redusă, nu respinsă |
from, to | Retenția planului | Fereastra temporală comună descrisă mai sus |
cursor | — | Cursor opac de paginare, din pagina anterioară |
botResult | — | Păstrează doar sesiunile cu acest verdict de bot |
minRiskScore | — | Păstrează doar sesiunile cu scor de risc egal sau mai mare, 0..100 |
Sesiunile vin cu cele mai noi primele:
{ "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" }}Paginarea se face pe bază de cursor. Nu există parametru page sau offset: trimiteți
înapoi drept cursor valoarea nextCursor primită și continuați cât timp hasMore
este 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}Tratați cursorul ca opac — conținutul lui este un detaliu de implementare și se poate
schimba. Un cursor care a fost modificat este respins cu 400 invalid_request și
mesajul malformed cursor.
Cea mai recentă:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Aceasta returnează un obiect sesiune simplu — nu un array și nu împachetat într-un plic.
Un vizitator fără sesiuni în fereastră returnează 404 not_found cu
no sessions for this visitor in the retention window.
Sau după requestId, identificatorul care apare și în payload-ul webhook-ului:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId este opțional aici, dar transmiterea lui, atunci când îl cunoașteți, face
căutarea vizibil mai rapidă.
Velocity răspunde la întrebarea „cât de mult a făcut acest vizitator în ultima vreme” — forma pe care o au credential stuffing-ul, testarea de carduri și înregistrările în masă.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window acceptă 1h, 24h sau 7d și are implicit 24h. Orice altă valoare este
respinsă cu 400 invalid_request și 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 numără valorile linkedId distincte pe care le-ați trimis pentru acest
dispozitiv — vedeți Asocierea conturilor. botEvents este
Business și peste.
Un câmp lipsă înseamnă „fără date”, niciodată zero. Câmpurile fără valoare sunt
omise complet, în loc să fie trimise ca 0, "" sau null: un vizitator nou-nouț nu
are matchConfidence, o vizită curată nu are antidetectScore sau suspectScore.
Singura excepție deliberată este bot.score, care este întotdeauna prezent, chiar și
când este zero. Citiți câmpurile defensiv.
Payload-ul depinde de planul dumneavoastră. Fiecare plan cu acces la API primește
sesiunea de bază — identificatori, timestamp, URL, IP, user agent, browser, sistem de
operare, dispozitiv, geo, rețea, bot, identificare și decizie. Pro adaugă
identification.matchType, identification.matchConfidence și bot.antidetectScore.
Business și Enterprise adaugă geo.isp, network.asn, decision.suspectScore,
identification.driftScore, reasons, behavior, guidance, deviceInfo și
câmpurile la nivel de persoană (personId, reputation, linkedAccountsCount,
linkedVisitorsCount). Absența unui câmp Business pe un plan Pro nu este o eroare.
Detaliile interne la nivel de semnal nu sunt returnate niciodată, la niciun plan: numele semnalelor individuale, ponderile lor, pragurile din spatele unui verdict, valorile brute ale semnalelor și descompunerea scorurilor rămân de partea noastră. Un scor care poate fi supus ingineriei inverse până la intrările sale încetează să mai fie util ca apărare.
Fiecare eșec folosește un singur plic:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}Acest requestId nu este identificatorul vizitei. Două valori diferite poartă
același nume: în payload-ul unei sesiuni, requestId este UUID-ul vizitei, același pe
care îl livrează webhook-ul; în plicul unei erori, este un identificator de urmărire de
24 de caractere, generat pentru fiecare apel HTTP. Identificatorul de urmărire se
întoarce și în antetul X-Request-Id la fiecare răspuns, reușit sau nu. Includeți-l
când contactați suportul — așa vă găsim apelul exact.
| HTTP | code | Semnificație |
|---|---|---|
| 400 | invalid_request | Un parametru lipsește sau este malformat |
| 401 | unauthorized | Cheia lipsește, este invalidă, revocată sau expirată |
| 402 | upgrade_required | Planul dumneavoastră nu include acces la API |
| 404 | not_found | Nimic nu s-a potrivit în fereastra de retenție |
| 405 | method_not_allowed | Ruta există, dar nu pentru acea metodă |
| 429 | rate_limited | S-au depășit cererile pe secundă sau cota zilnică |
| 500 | internal | Ceva a eșuat de partea noastră |
| 503 | unavailable | Un depozit de date suport este temporar inaccesibil |
Verificările se fac într-o ordine fixă — cheia, apoi planul, apoi limitele — așa că o cerere cu o cheie greșită raportează întotdeauna mai întâi cheia, niciodată o problemă de cotă.
Două cazuri de 401 se citesc diferit în mod intenționat:
missing Authorization: Bearer <secret key> înseamnă că antetul nu a sosit niciodată,
în timp ce invalid or revoked API key înseamnă că a sosit și nu s-a potrivit. 402
poartă Data API requires the Pro plan or higher.
Fiecare răspuns autentificat poartă situația dumneavoastră curentă:
| Antet | Semnificație |
|---|---|
X-RateLimit-Limit | Cota dumneavoastră zilnică |
X-RateLimit-Remaining | Apeluri rămase astăzi |
X-RateLimit-Reset | Timpul Unix al resetării — miezul nopții UTC |
Retry-After | Secunde de așteptat, trimis doar cu un 429 |
| Plan | Cereri pe secundă | Cereri pe zi | Adâncimea istoricului |
|---|---|---|---|
| Free | Fără acces la API | — | 7 zile |
| Pro | 10 | 10.000 | 30 de zile |
| Business | 50 | 100.000 | 90 de zile |
| Enterprise | 200 | Nelimitat | 365 de zile |