Server API memungkinkan backend Anda membaca data identifikasi yang sudah dikumpulkan TRACIO untuk workspace Anda: riwayat seorang pengunjung, sesi satu per satu, dan penghitung velocity pada jendela pendek.
Ia melengkapi Webhook, bukan menggantikannya:
| Webhooks | Server API | |
|---|---|---|
| Arah | TRACIO mendorong ke endpoint Anda | Backend Anda menarik saat dibutuhkan |
| Waktu | Saat setiap identifikasi terjadi | Kapan saja, dalam jendela retensi Anda |
| Paling cocok untuk | Bereaksi terhadap sebuah peristiwa | Mencari data saat mengambil keputusan, pengisian ulang, investigasi |
Kedua permukaan tersedia mulai paket Pro ke atas.
https://api.tracio.ai/v1Ini adalah host yang berbeda dari endpoint peramban (edge.tracio.ai) dan dari dashboard
(app.tracio.ai). Ketiganya terpisah: peramban berbicara dengan edge memakai kunci
public Anda, sedangkan backend Anda berbicara dengan Server API memakai kunci
secret Anda.
Setiap permintaan membawa secret key Anda sebagai bearer token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Server API hanya untuk komunikasi server ke server. Header CORS sengaja tidak dikembalikan, sehingga peramban tidak dapat memanggilnya — itulah yang menjaga secret key Anda tetap di luar kode sisi klien. Jangan pernah mengirimkan secret key ke peramban.
Buat di dashboard pada bagian API Keys, dengan memilih tipe secret.
tracio_sk_ diikuti 43 karakter, total 53. Dashboard
menampilkannya berdasarkan beberapa karakter pertama sehingga Anda bisa membedakan
antar kunci.Rotasi menerbitkan kunci baru dan membiarkan kunci lama tetap bekerja selama 7 hari, sehingga Anda bisa menggulirkannya tanpa waktu henti. Terapkan kunci baru, pastikan trafiknya sudah berpindah, lalu biarkan kunci lama kedaluwarsa. Public key tidak dapat dirotasi — kunci itu bukan rahasia dan memang terlihat di sumber halaman Anda.
Setiap route adalah GET. Tidak ada operasi tulis di Server API: ia membaca data, dan
konfigurasi Anda berada di dashboard.
| Metode | Path | Mengembalikan |
|---|---|---|
GET | /v1/visitors/{visitorId} | Riwayat teragregasi satu pengunjung, plus sesi terbarunya |
GET | /v1/visitors/{visitorId}/sessions | Daftar terpaginasi sesi-sesi pengunjung tersebut |
GET | /v1/visitors/{visitorId}/sessions/latest | Satu sesi paling baru saja |
GET | /v1/visitors/{visitorId}/velocity | Penghitung aktivitas pada jendela pendek |
GET | /v1/sessions/{requestId} | Satu sesi berdasarkan pengidentifikasi permintaannya |
GET | /.well-known/webhook-keys | Kunci publik untuk tanda tangan platform webhook (tanpa autentikasi) |
Garis miring di akhir diterima dan diabaikan. Path yang tidak dikenal atau metode yang salah mengembalikan amplop error JSON yang sama seperti segala hal lainnya, tidak pernah berupa halaman HTML atau teks biasa.
Setiap pembacaan dibatasi oleh sebuah jendela waktu, yang dikendalikan dua parameter query opsional:
| Parameter | Menerima |
|---|---|
from | YYYY-MM-DD atau timestamp RFC 3339 lengkap |
to | YYYY-MM-DD atau timestamp RFC 3339 lengkap |
to mencakup seluruh hari itu.400 invalid_request dan pesan
time must be YYYY-MM-DD or RFC3339.meta, jadi periksalah meta.from dan meta.to alih-alih
menganggap permintaan Anda dipenuhi apa adanya.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Responsnya membawa riwayat agregat dan menyertakan sesi terbaru, sehingga kasus umum cukup dengan satu permintaan alih-alih dua:
{ "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" }}| Field | Arti |
|---|---|
visits, incognitoVisits | Total kunjungan dalam jendela, dan berapa yang terjadi di jendela pribadi |
uniqueIps, uniqueCountries | Alamat dan negara berbeda yang terlihat dalam jendela |
browsers, os, devices | Lingkungan berbeda tempat pengunjung ini pernah muncul |
risk.maxRiskScore | Skor risiko tertinggi yang tercatat dalam jendela, 0..100 |
risk.lastDecision | Keputusan yang tercatat untuk kunjungan paling baru |
risk.avgBotScore, risk.botSessions | Rata-rata skor bot dan jumlah sesi bot — Business ke atas |
network.*Seen | Apakah VPN, proxy, node keluar Tor, atau alamat datacenter pernah terlihat pada pengunjung ini |
network.lastIsp | ISP paling baru — Business ke atas |
lastSession | Objek sesi lengkap untuk kunjungan paling baru |
meta | Paket, retensinya dalam hari, dan jendela yang benar-benar diterapkan |
Pengunjung tanpa data di dalam jendela retensi mengembalikan 404 not_found dengan pesan
visitor not found in the retention window — itu bukan error pada integrasi Anda,
melainkan berarti pengunjungnya baru atau datanya sudah melewati masa retensi.
Sesi membawa dua verdict, dan keduanya menjawab pertanyaan berbeda — apakah kliennya otomatis, dan apa kesimpulan keseluruhan dari mesin risiko:
| Field | Nilai |
|---|---|
bot.result | human, bot, uncertain |
decision.action | real, fake, suspicious |
bot.score dan decision.riskScore sama-sama berjalan pada 0..100. Pada Business ke
atas, guidance mengubahnya menjadi saran per skenario pada tangga
allow → challenge → review → deny — lihat
Guidance untuk arti setiap anak tangganya.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Parameter | Default | Catatan |
|---|---|---|
limit | 50 | Dibatasi di 500; nilai lebih besar dipangkas, bukan ditolak |
from, to | Retensi paket | Jendela waktu bersama yang dijelaskan di atas |
cursor | — | Kursor paginasi buram dari halaman sebelumnya |
botResult | — | Hanya simpan sesi dengan verdict bot ini |
minRiskScore | — | Hanya simpan sesi pada skor risiko ini atau di atasnya, 0..100 |
Sesi dikembalikan dari yang terbaru lebih dulu:
{ "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" }}Paginasi berbasis kursor. Tidak ada parameter page atau offset: kirimkan kembali
nextCursor yang Anda terima sebagai cursor, dan teruskan selama hasMore bernilai
benar.
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}Perlakukan kursor sebagai buram — isinya adalah detail implementasi dan bisa berubah.
Kursor yang telah disunting ditolak dengan 400 invalid_request dan pesan
malformed cursor.
Yang paling baru:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Ini mengembalikan objek sesi telanjang — bukan array, dan tidak dibungkus dalam amplop.
Pengunjung tanpa sesi apa pun dalam jendela mengembalikan 404 not_found dengan
no sessions for this visitor in the retention window.
Atau berdasarkan requestId, pengidentifikasi yang juga muncul pada payload webhook:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId bersifat opsional di sini, tetapi mengirimkannya ketika Anda mengetahuinya
membuat pencariannya jauh lebih cepat.
Velocity menjawab pertanyaan "seberapa banyak yang dilakukan pengunjung ini belakangan ini" — bentuk khas dari credential stuffing, uji coba kartu, dan pendaftaran massal.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window menerima 1h, 24h, atau 7d dan secara default 24h. Nilai lain apa pun
ditolak dengan 400 invalid_request dan 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 menghitung nilai linkedId berbeda yang Anda kirim untuk perangkat ini
— lihat Penautan akun. botEvents tersedia mulai Business ke
atas.
Field yang tidak ada berarti "tidak ada data", tidak pernah berarti nol. Field tanpa
nilai dihilangkan sepenuhnya alih-alih dikirim sebagai 0, "", atau null: pengunjung
yang benar-benar baru tidak punya matchConfidence, kunjungan yang bersih tidak punya
antidetectScore atau suspectScore. Satu-satunya pengecualian yang disengaja adalah
bot.score, yang selalu ada bahkan ketika nilainya nol. Bacalah field secara defensif.
Payload bergantung pada paket Anda. Setiap paket dengan akses API mendapatkan sesi
dasar — pengidentifikasi, timestamp, URL, IP, user agent, peramban, OS, perangkat, geo,
network, bot, identification, dan decision. Pro menambahkan
identification.matchType, identification.matchConfidence, dan
bot.antidetectScore. Business dan Enterprise menambahkan geo.isp, network.asn,
decision.suspectScore, identification.driftScore, reasons, behavior, guidance,
deviceInfo, serta field tingkat orang (personId, reputation,
linkedAccountsCount, linkedVisitorsCount). Tidak adanya field Business pada paket Pro
bukanlah sebuah error.
Bagian internal pada tingkat sinyal tidak pernah dikembalikan, pada paket mana pun: nama masing-masing sinyal, bobotnya, ambang batas di balik sebuah verdict, nilai sinyal mentah, dan rincian skor tetap berada di sisi kami. Skor yang bisa direkayasa balik menjadi masukannya berhenti berguna sebagai pertahanan.
Setiap kegagalan memakai satu amplop:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}requestId ini bukan pengidentifikasi kunjungan. Dua nilai berbeda berbagi nama yang
sama: di dalam payload sesi, requestId adalah UUID kunjungan, sama dengan yang dikirim
webhook; di dalam amplop error, ia adalah pengidentifikasi jejak sepanjang 24 karakter
yang dibuat untuk setiap panggilan HTTP. Pengidentifikasi jejak itu juga kembali di
header X-Request-Id pada setiap respons, berhasil maupun tidak. Sertakan nilainya saat
Anda menghubungi dukungan — dari situlah kami menemukan panggilan Anda yang persis.
| HTTP | code | Arti |
|---|---|---|
| 400 | invalid_request | Sebuah parameter hilang atau salah bentuk |
| 401 | unauthorized | Kuncinya tidak ada, tidak valid, dicabut, atau kedaluwarsa |
| 402 | upgrade_required | Paket Anda tidak mencakup akses API |
| 404 | not_found | Tidak ada yang cocok di dalam jendela retensi |
| 405 | method_not_allowed | Route-nya ada, tetapi tidak untuk metode itu |
| 429 | rate_limited | Permintaan per detik, atau kuota harian, terlampaui |
| 500 | internal | Ada yang gagal di sisi kami |
| 503 | unavailable | Sebuah penyimpanan pendukung sementara tidak terjangkau |
Pemeriksaan berjalan dalam urutan tetap — kunci, lalu paket, lalu batas — sehingga permintaan dengan kunci yang salah selalu melaporkan soal kunci lebih dulu, tidak pernah soal kuota.
Dua kasus 401 sengaja dibaca berbeda: missing Authorization: Bearer <secret key>
berarti header-nya tidak pernah tiba, sedangkan invalid or revoked API key berarti ia
tiba dan tidak cocok. 402 membawa Data API requires the Pro plan or higher.
Setiap respons terautentikasi membawa posisi Anda saat ini:
| Header | Arti |
|---|---|
X-RateLimit-Limit | Kuota harian Anda |
X-RateLimit-Remaining | Panggilan yang tersisa hari ini |
X-RateLimit-Reset | Waktu Unix saat reset — tengah malam UTC |
Retry-After | Detik yang harus ditunggu, hanya dikirim dengan 429 |
| Paket | Permintaan per detik | Permintaan per hari | Kedalaman riwayat |
|---|---|---|---|
| Free | Tanpa akses API | — | 7 hari |
| Pro | 10 | 10.000 | 30 hari |
| Business | 50 | 100.000 | 90 hari |
| Enterprise | 200 | Tanpa penghitungan | 365 hari |