Met de Data API (hier eerder gedocumenteerd als de Server API) kan uw backend de identificatiegegevens lezen die TRACIO al voor uw workspace heeft verzameld: de geschiedenis van een bezoeker, afzonderlijke sessies en velocity-tellers over korte vensters.
Hij vult webhooks aan in plaats van ze te vervangen:
| Webhooks | Data API | |
|---|---|---|
| Richting | TRACIO pusht naar uw endpoint | Uw backend haalt op wanneer nodig |
| Timing | Zodra elke identificatie plaatsvindt | Op elk moment, binnen uw bewaarvenster |
| Het best voor | Reageren op een event | Gegevens opzoeken tijdens een beslissing, backfills, onderzoeken |
Beide interfaces zijn beschikbaar vanaf het Pro-abonnement en hoger.
https://api.tracio.ai/v1Dit is een andere host dan het browser-endpoint (edge.tracio.ai) en dan het dashboard
(app.tracio.ai). Alle drie staan los van elkaar: de browser praat met de edge met uw
publieke sleutel, uw backend praat met de Data API met uw geheime sleutel.
Elk verzoek draagt uw geheime sleutel als bearer-token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"De Data API is uitsluitend server-naar-server. CORS-headers worden bewust niet teruggegeven, zodat een browser hem niet kan aanroepen — precies dat houdt uw geheime sleutel buiten client-side code. Lever de geheime sleutel nooit uit aan de browser.
Maak hem aan in het dashboard onder API Keys en kies daarbij het type secret.
tracio_sk_ gevolgd door 43 tekens, 53 in totaal.
Het dashboard toont hem aan de hand van zijn eerste tekens, zodat u sleutels uit
elkaar kunt houden.Bij roteren wordt een nieuwe sleutel uitgegeven terwijl de oude nog 7 dagen blijft werken, zodat u hem zonder downtime kunt uitrollen. Rol de nieuwe sleutel uit, controleer dat het verkeer is verhuisd, en laat de oude verlopen. Publieke sleutels kunnen niet worden geroteerd — het zijn geen geheimen en ze staan met opzet zichtbaar in de broncode van uw pagina.
Elke route is een GET. Er zijn geen schrijfoperaties in de Data API: hij leest
gegevens, en uw configuratie leeft in het dashboard.
| Methode | Pad | Geeft terug |
|---|---|---|
GET | /v1/visitors/{visitorId} | Geaggregeerde geschiedenis van één bezoeker, plus diens laatste sessie |
GET | /v1/visitors/{visitorId}/sessions | Gepagineerde lijst van de sessies van die bezoeker |
GET | /v1/visitors/{visitorId}/sessions/latest | De enkele meest recente sessie |
GET | /v1/visitors/{visitorId}/velocity | Activiteitstellers over een kort venster |
GET | /v1/sessions/{requestId} | Eén sessie op basis van zijn request-identificatie |
GET | /.well-known/webhook-keys | Publieke sleutels voor de platformhandtekening van webhooks (geen auth) |
Een afsluitende slash wordt geaccepteerd en genegeerd. Een onbekend pad of een verkeerde methode geeft dezelfde JSON-foutenvelop terug als al het andere, nooit een HTML- of platte-tekstpagina.
Elke leesactie wordt begrensd door een tijdvenster, gestuurd door twee optionele query-parameters:
| Parameter | Accepteert |
|---|---|
from | YYYY-MM-DD of een volledige RFC 3339-tijdstempel |
to | YYYY-MM-DD of een volledige RFC 3339-tijdstempel |
to omvat die hele dag.400 invalid_request en het bericht
time must be YYYY-MM-DD or RFC3339.meta, dus controleer meta.from en meta.to in plaats van
aan te nemen dat uw verzoek letterlijk is ingewilligd.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"De response draagt de geaggregeerde geschiedenis en bevat de laatste sessie, zodat het gangbare geval één verzoek nodig heeft in plaats van twee:
{ "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, "proxyDetectedSeen": true, "lastIsp": "Deutsche Telekom", "lastRealIp": "203.0.113.7" }, "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", "gpu": "Apple M2", "geo": { "country": "DE", "city": "Berlin", "timezone": "Europe/Berlin", "isp": "Deutsche Telekom" }, "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "proxyDetected": true, "realIp": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" }, "asn": 3320 }, "screen": { "width": 2560, "height": 1600, "colorDepth": 30, "pixelRatio": 2 }, "locale": { "languages": ["en-US", "de"], "timezone": "Europe/Berlin" }, "clientHints": { "architecture": "arm", "bitness": "64", "platformVersion": "14.5.0" }, "extensions": [ { "slug": "ublock-origin", "name": "uBlock Origin", "category": "adblock", "risky": false, "storeUrl": "https://chromewebstore.google.com/detail/cjpalhdlnbpafiamejdnhcphjbkeiagm" } ], "bot": { "result": "human", "score": 4.5, "antidetectScore": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "matchType": "exact", "matchConfidence": 0.99 }, "deviceInfo": { "deviceId": "d_4f9c2e", "crossBrowser": true, "confidence": 0.88, "linkedBrowsers": 2 }, "decision": { "action": "real", "riskScore": 12 } }, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| Veld | Betekenis |
|---|---|
visits, incognitoVisits | Totaal aantal bezoeken in het venster, en hoeveel daarvan in een privévenster waren |
uniqueIps, uniqueCountries | Verschillende adressen en landen die in het venster zijn gezien |
browsers, os, devices | De verschillende omgevingen waarin deze bezoeker is verschenen |
risk.maxRiskScore | De hoogste risicoscore die in het venster is vastgelegd, 0..100 |
risk.lastDecision | De beslissing die voor het meest recente bezoek is vastgelegd |
risk.avgBotScore, risk.botSessions | Gemiddelde bot-score en het aantal bot-sessies — vanaf Business |
network.*Seen | Of er voor deze bezoeker ooit een VPN, proxy, Tor-exitnode of datacenteradres is gezien |
network.proxyDetectedSeen | Of ten minste één bezoek in het venster naar buiten kwam via een proxy of VPN vóór de browser — zie network.proxyDetected onder Apparaatfeiten |
network.lastIsp | De meest recente ISP — vanaf Business |
network.lastRealIp | Het meest recente adres dat achter een proxy of VPN is waargenomen — vanaf Business; ontbreekt wanneer er geen is waargenomen |
lastSession | Het volledige sessie-object van het meest recente bezoek |
meta | Het abonnement, de bewaartermijn in dagen en het venster dat daadwerkelijk is toegepast |
Een bezoeker zonder gegevens binnen het bewaarvenster geeft 404 not_found met het
bericht visitor not found in the retention window — dat is geen fout in uw
integratie, het betekent dat de bezoeker nieuw is of buiten de termijn is gevallen.
De sessie draagt twee verdicts, en die beantwoorden verschillende vragen — of de client geautomatiseerd was, en wat de risico-engine in het geheel concludeerde:
| Veld | Waarden |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Aanwezig wanneer bot.result bot is: ofwel een specifieke tool (playwright, puppeteer, selenium, jsdom, claude_computer_use…), ofwel een familie wanneer de tool niet wordt benoemd — automation, headless, antidetect, extension, privacy_browser, other |
decision.action | real, fake, suspicious |
bot.score en decision.riskScore lopen allebei van 0..100. Vanaf Business zet
guidance ze om in advies per scenario op de ladder
allow → challenge → review → deny — zie
Guidance voor wat elke trede betekent.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Parameter | Standaard | Opmerkingen |
|---|---|---|
limit | 50 | Gemaximeerd op 500; een grotere waarde wordt ingekort, niet geweigerd |
from, to | Bewaartermijn van abonnement | Het gedeelde tijdvenster dat hierboven is beschreven |
cursor | — | Ondoorzichtige paginatiecursor van de vorige pagina |
botResult | — | Houd alleen sessies met dit bot-verdict over |
minRiskScore | — | Houd alleen sessies op of boven deze risicoscore over, 0..100 |
Sessies komen terug met de nieuwste eerst:
{ "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" }}Bladeren gaat op basis van een cursor. Er is geen parameter page of offset: geef de
nextCursor die u hebt ontvangen terug als cursor, en ga door zolang hasMore waar
is.
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(`Data API: ${res.status}`)
const page = await res.json() sessions.push(...page.items) cursor = page.nextCursor } while (cursor)
return sessions}Behandel de cursor als ondoorzichtig — de inhoud ervan is een implementatiedetail en
kan veranderen. Een cursor die is bewerkt, wordt geweigerd met 400 invalid_request en
het bericht malformed cursor.
Naast de browser en het besturingssysteem uit de User-Agent draagt een sessie wat de browser van de bezoeker over de machine meldt, aan onze kant opgeschoond. Elk veld ontbreekt wanneer het bezoek geen dergelijke gegevens meebracht, dus behandel elk als optioneel.
| Veld | Betekenis |
|---|---|
gpu | Model van de videoadapter zoals de browser die meldt (WebGL), genormaliseerd naar een leesbare naam — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer betekent geen echte GPU (een virtuele machine of een headless omgeving); Safari meldt Apple GPU |
network.proxyDetected | Het HTTP-verkeer van het bezoek en de rauwe netwerkpaden ervan komen via verschillende netwerken naar buiten — een proxy of VPN vóór de browser; twee adressen van dezelfde provider (carrier-NAT, een tweede uitgang van hetzelfde VPN) tellen niet mee |
network.realIp.address, .country, .isp | Het publieke adres dat op het rauwe netwerkpad is waargenomen, dat wil zeggen het adres achter de proxy of VPN, met het bijbehorende land en de ISP — vanaf Business; ontbreekt wanneer zo'n adres niet is waargenomen (country en isp ontbreken wanneer ze niet konden worden herleid) |
deviceInfo.deviceId, .crossBrowser, .confidence, .linkedBrowsers | Aanwezig wanneer een apparaatidentiteit is vastgesteld: een stabiele id van het fysieke apparaat over de browsers erop heen, of dit bezoek via een andere browser kwam dan eerder, de betrouwbaarheid van die match en hoeveel verschillende bezoekers (browsers) het apparaat delen — meer dan één betekent één machine onder meerdere browseridentiteiten — vanaf Business |
osEnvironment | De desktopomgeving die op een Linux-machine is gemeten (Mint 22+, Ubuntu, GNOME, KDE) — vanaf Business; ontbreekt wanneer die niet is vastgesteld |
spoofing | Wat het bezoek beweerde, tegenover wat onafhankelijke controles hebben gemeten (claimed, real, spoofedAxes uit os, gpu, screen, network, browser; anonymousBrowser met productnamen) — vanaf Business; alleen aanwezig wanneer er een spoof is gedetecteerd |
screen.width, .height, .colorDepth, .pixelRatio | Schermresolutie, kleurdiepte en device pixel ratio zoals de browser die meldt — vanaf Business |
locale.languages, locale.timezone | De eigen voorkeurstalen en tijdzone van de browser — in tegenstelling tot geo.timezone, dat uit het IP-adres wordt afgeleid; een verschil tussen die twee is een veelvoorkomend teken van een vervalste locatie — vanaf Business |
clientHints.architecture, .bitness, .model, .deviceName, .platformVersion | User-Agent Client Hints: CPU-architectuur en bitness, apparaatmodelcode (Android, bijv. SM-A556B) met de marketingnaam ervan uit de Google Play-apparaatlijst (deviceName, bijv. Samsung Galaxy A55 5G) en de exacte platformversie; alleen Chromium-gebaseerde browsers — vanaf Business |
environment.virtualMachine, environment.hypervisor | Alleen aanwezig wanneer de videoadapter zichzelf als virtueel bekendmaakte; hypervisor is een gesloten woordenboek (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Een ontbrekend blok betekent dat er geen dergelijke aanwijzing is — vanaf Business |
extensions somt de browserextensies op die tijdens het bezoek zijn gedetecteerd — vanaf Business. Elke vermelding is een object:
| Veld | Betekenis |
|---|---|
slug | Stabiele, machineleesbare identificatie van de extensie, dezelfde waarde die de webhook levert |
name | Voor mensen leesbare naam |
category | Grove klasse — adblock, privacy, automation, wallet, vpn, devtools, other, enzovoort |
risky | true voor extensies die in verband worden gebracht met automatisering, spoofing of diefstal van inloggegevens |
storeUrl | Link naar de storevermelding van de extensie, wanneer die bekend is |
Een bevinding wordt pas gemeld nadat die onze betrouwbaarheidscontroles heeft doorstaan — een omgeving die op elke test “geïnstalleerd” antwoordt, of een reeks van meer dan twaalf namen, wordt als onbetrouwbaar verworpen. Een lege of ontbrekende lijst betekent daarom “niets wat wij konden bevestigen”, niet “geen extensies geïnstalleerd”. Lees hem als aanwijzing, niet als inventaris.
De meest recente:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Dit geeft een kaal sessie-object terug — geen array, en niet verpakt in een envelop.
Een bezoeker zonder sessies in het venster geeft 404 not_found met
no sessions for this visitor in the retention window.
Of op requestId, de identificatie die ook in de webhook-payload verschijnt:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId is hier optioneel, maar hem meegeven wanneer u hem kent, maakt het opzoeken
merkbaar sneller.
Velocity beantwoordt de vraag “hoeveel heeft deze bezoeker de laatste tijd gedaan” — de vorm van credential stuffing, card testing en registraties in bulk.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window accepteert 1h, 24h of 7d en is standaard 24h. Elke andere waarde wordt
geweigerd met 400 invalid_request en 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 telt de verschillende linkedId-waarden die u voor dit apparaat hebt
gestuurd — zie Accounts koppelen. botEvents is vanaf
Business.
Een ontbrekend veld betekent “geen gegevens”, nooit nul. Velden zonder waarde
worden volledig weggelaten in plaats van verstuurd als 0, "" of null: een
gloednieuwe bezoeker heeft geen matchConfidence, een schoon bezoek heeft geen
antidetectScore of suspectScore. De ene bewuste uitzondering is bot.score, dat
altijd aanwezig is, zelfs wanneer het nul is. Lees velden defensief.
De payload hangt af van uw abonnement. Elk abonnement met API-toegang krijgt de basissessie — identificaties, tijdstempel, URL, IP, user agent, browser, besturingssysteem, apparaat, geo, netwerk, bot, identificatie en beslissing. Pro voegt identification.matchType, identification.matchConfidence, bot.type, bot.antidetectScore, gpu en network.proxyDetected toe. Business en Enterprise voegen extensions, geo.isp, network.asn, network.realIp, decision.suspectScore, identification.driftScore, reasons, behavior, guidance, deviceInfo, spoofing, osEnvironment, screen, locale, clientHints en environment toe. De velden op persoonsniveau (personId, reputation, linkedAccountsCount, linkedVisitorsCount) zijn voorbehouden aan Business en Enterprise en verschijnen zodra de persoonslaag is ingeschakeld — vandaag draait die in observatiemodus en worden deze velden niet geleverd. Het ontbreken van een Business-veld op een Pro-abonnement is geen fout.
Interne details op signaalniveau worden nooit teruggegeven, op geen enkel abonnement: afzonderlijke signaalnamen, hun gewichten, de drempels achter een verdict, ruwe signaalwaarden en de opbouw van scores blijven aan onze kant. Een score die via reverse engineering tot zijn invoer te herleiden is, houdt op nuttig te zijn als verdediging.
Elke mislukking gebruikt één envelop:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}Deze requestId is niet de identificatie van het bezoek. Twee verschillende
waarden delen de naam: binnen een sessie-payload is requestId de UUID van het bezoek,
dezelfde die de webhook levert; binnen een foutenvelop is het een trace-identificatie
van 24 tekens die per HTTP-aanroep wordt aangemaakt. De trace-identificatie komt ook
terug in de header X-Request-Id bij elke response, geslaagd of niet. Vermeld hem
wanneer u contact opneemt met support — zo vinden wij precies uw aanroep.
| HTTP | code | Betekenis |
|---|---|---|
| 400 | invalid_request | Een parameter ontbreekt of is misvormd |
| 401 | unauthorized | De sleutel ontbreekt, is ongeldig, ingetrokken of verlopen |
| 402 | upgrade_required | Uw abonnement bevat geen API-toegang |
| 404 | not_found | Er kwam niets overeen binnen het bewaarvenster |
| 405 | method_not_allowed | De route bestaat, maar niet voor die methode |
| 429 | rate_limited | Verzoeken per seconde, of de dagelijkse quota, overschreden |
| 500 | internal | Er is iets misgegaan aan onze kant |
| 503 | unavailable | Een onderliggende opslag is tijdelijk onbereikbaar |
De controles lopen in een vaste volgorde — sleutel, dan abonnement, dan limieten — zodat een verzoek met een verkeerde sleutel altijd eerst de sleutel meldt, nooit een quotaprobleem.
Twee 401-gevallen lezen met opzet anders: missing Authorization: Bearer <secret key>
betekent dat de header nooit is aangekomen, terwijl invalid or revoked API key
betekent dat hij is aangekomen en niet overeenkwam. 402 draagt
Data API requires the Pro plan or higher.
Elke geauthenticeerde response draagt uw huidige stand:
| Header | Betekenis |
|---|---|
X-RateLimit-Limit | Uw dagelijkse quota |
X-RateLimit-Remaining | Aanroepen die vandaag nog over zijn |
X-RateLimit-Reset | Unix-tijd van de reset — middernacht UTC |
Retry-After | Aantal seconden wachten, alleen verstuurd bij een 429 |
| Abonnement | Verzoeken per seconde | Verzoeken per dag | Diepte van de geschiedenis |
|---|---|---|---|
| Free | Geen API-toegang | — | 7 dagen |
| Pro | 10 | 10.000 | 30 dagen |
| Business | 50 | 100.000 | 90 dagen |
| Enterprise | 200 | Zonder quotum | 365 dagen |