Server API cho phép backend của bạn đọc dữ liệu nhận diện mà TRACIO đã thu thập cho workspace của bạn: lịch sử của một khách truy cập, từng phiên riêng lẻ, và các bộ đếm velocity trên cửa sổ ngắn.
Nó bổ sung cho Webhooks chứ không thay thế chúng:
| Webhooks | Server API | |
|---|---|---|
| Hướng | TRACIO đẩy đến endpoint của bạn | Backend của bạn kéo về khi cần |
| Thời điểm | Ngay khi mỗi lần nhận diện xảy ra | Bất cứ lúc nào, trong cửa sổ lưu giữ của bạn |
| Phù hợp nhất cho | Phản ứng với một sự kiện | Tra cứu dữ liệu khi ra quyết định, bù dữ liệu, điều tra |
Cả hai bề mặt đều có từ gói Pro trở lên.
https://api.tracio.ai/v1Đây là một host khác với endpoint dành cho trình duyệt (edge.tracio.ai) và khác với
dashboard (app.tracio.ai). Cả ba đều tách biệt: trình duyệt nói chuyện với edge bằng
khóa public của bạn, còn backend của bạn nói chuyện với Server API bằng khóa
secret của bạn.
Mỗi yêu cầu đều mang theo secret key của bạn dưới dạng bearer token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Server API chỉ dành cho giao tiếp máy chủ với máy chủ. Các header CORS cố ý không được trả về, nên trình duyệt không thể gọi nó — đó chính là điều giữ secret key của bạn nằm ngoài mã phía client. Đừng bao giờ đưa secret key xuống trình duyệt.
Hãy tạo nó trong dashboard ở mục API Keys, chọn loại secret.
tracio_sk_ theo sau là 43 ký tự, tổng cộng 53. Dashboard liệt
kê nó bằng vài ký tự đầu tiên để bạn phân biệt được các khóa với nhau.Việc xoay vòng phát hành một khóa mới và giữ cho khóa cũ vẫn hoạt động trong 7 ngày, nhờ đó bạn có thể triển khai mà không phải ngừng dịch vụ. Hãy triển khai khóa mới, xác nhận lưu lượng đã chuyển sang, rồi để khóa cũ hết hạn. Public key không xoay vòng được — chúng không phải bí mật và vốn dĩ hiển thị ngay trong mã nguồn trang của bạn.
Mọi route đều là GET. Server API không có thao tác ghi nào: nó đọc dữ liệu, còn cấu
hình của bạn nằm trong dashboard.
| Phương thức | Đường dẫn | Trả về |
|---|---|---|
GET | /v1/visitors/{visitorId} | Lịch sử tổng hợp của một khách truy cập, cùng phiên mới nhất |
GET | /v1/visitors/{visitorId}/sessions | Danh sách phân trang các phiên của khách truy cập đó |
GET | /v1/visitors/{visitorId}/sessions/latest | Đúng một phiên gần đây nhất |
GET | /v1/visitors/{visitorId}/velocity | Các bộ đếm hoạt động trên một cửa sổ ngắn |
GET | /v1/sessions/{requestId} | Một phiên theo định danh yêu cầu của nó |
GET | /.well-known/webhook-keys | Khóa công khai cho chữ ký nền tảng webhook (không cần xác thực) |
Dấu gạch chéo ở cuối được chấp nhận và bị bỏ qua. Một đường dẫn không xác định hoặc một phương thức sai sẽ trả về đúng cái phong bì lỗi JSON như mọi thứ khác, không bao giờ là trang HTML hay văn bản thuần.
Mỗi lần đọc đều bị giới hạn bởi một cửa sổ thời gian, do hai tham số truy vấn tùy chọn điều khiển:
| Tham số | Chấp nhận |
|---|---|
from | YYYY-MM-DD hoặc dấu thời gian RFC 3339 |
to | YYYY-MM-DD hoặc dấu thời gian RFC 3339 |
to sẽ bao gồm trọn cả ngày hôm đó.400 invalid_request và thông báo
time must be YYYY-MM-DD or RFC3339.meta, vì vậy
hãy kiểm tra meta.from và meta.to thay vì mặc định rằng yêu cầu của bạn được tôn
trọng nguyên văn.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Phản hồi mang theo lịch sử tổng hợp và nhúng luôn phiên mới nhất, nhờ đó trường hợp thông thường chỉ cần một yêu cầu thay vì hai:
{ "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" }}| Trường | Ý nghĩa |
|---|---|
visits, incognitoVisits | Tổng số lượt truy cập trong cửa sổ, và bao nhiêu lượt diễn ra ở cửa sổ riêng tư |
uniqueIps, uniqueCountries | Số địa chỉ và số quốc gia khác nhau đã thấy trong cửa sổ |
browsers, os, devices | Các môi trường khác nhau mà khách truy cập này từng xuất hiện |
risk.maxRiskScore | Điểm rủi ro cao nhất được ghi nhận trong cửa sổ, 0..100 |
risk.lastDecision | Quyết định được ghi nhận cho lượt truy cập gần đây nhất |
risk.avgBotScore, risk.botSessions | Điểm bot trung bình và số phiên là bot — từ Business trở lên |
network.*Seen | Đã từng thấy VPN, proxy, node thoát Tor hay địa chỉ datacenter cho khách này chưa |
network.lastIsp | ISP gần đây nhất — từ Business trở lên |
lastSession | Đối tượng phiên đầy đủ của lượt truy cập gần đây nhất |
meta | Gói, thời gian lưu giữ tính theo ngày, và cửa sổ thực sự được áp dụng |
Một khách truy cập không có dữ liệu nào bên trong cửa sổ lưu giữ sẽ trả về 404 not_found
với thông báo visitor not found in the retention window — đó không phải lỗi trong tích
hợp của bạn, mà nghĩa là khách truy cập đó còn mới hoặc dữ liệu đã hết hạn lưu giữ.
Phiên mang theo hai phán quyết, và chúng trả lời những câu hỏi khác nhau — client có phải là tự động hay không, và cỗ máy đánh giá rủi ro kết luận tổng thể ra sao:
| Trường | Giá trị |
|---|---|
bot.result | human, bot, uncertain |
decision.action | real, fake, suspicious |
Cả bot.score lẫn decision.riskScore đều chạy trên thang 0..100. Từ Business trở
lên, guidance biến chúng thành khuyến nghị theo từng kịch bản trên thang
allow → challenge → review → deny — xem
Guidance để biết ý nghĩa của từng nấc.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Tham số | Mặc định | Ghi chú |
|---|---|---|
limit | 50 | Chặn ở 500; giá trị lớn hơn bị cắt gọn chứ không bị từ chối |
from, to | Lưu giữ theo gói | Cửa sổ thời gian dùng chung đã mô tả ở trên |
cursor | — | Con trỏ phân trang mờ đục nhận được từ trang trước |
botResult | — | Chỉ giữ những phiên có phán quyết bot này |
minRiskScore | — | Chỉ giữ những phiên có điểm rủi ro từ mức này trở lên, 0..100 |
Các phiên trở về theo thứ tự mới nhất trước:
{ "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" }}Việc phân trang dựa trên con trỏ. Không có tham số page hay offset: hãy truyền lại
nextCursor bạn nhận được dưới dạng cursor, và cứ tiếp tục chừng nào hasMore còn
đúng.
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}Hãy coi con trỏ là mờ đục — nội dung của nó là chi tiết triển khai và có thể thay đổi.
Một con trỏ đã bị sửa sẽ bị từ chối với 400 invalid_request và thông báo
malformed cursor.
Phiên gần đây nhất:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Endpoint này trả về một đối tượng phiên trần — không phải mảng, và không được bọc trong
phong bì nào. Một khách truy cập không có phiên nào trong cửa sổ sẽ trả về
404 not_found với no sessions for this visitor in the retention window.
Hoặc theo requestId, chính là định danh cũng xuất hiện trong payload của webhook:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Ở đây visitorId là tùy chọn, nhưng truyền nó khi bạn đã biết sẽ làm cho việc tra cứu
nhanh hơn đáng kể.
Velocity trả lời câu hỏi "gần đây khách truy cập này đã làm nhiều đến mức nào" — chính là hình dạng của credential stuffing, thử thẻ và đăng ký hàng loạt.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window chấp nhận 1h, 24h hoặc 7d và mặc định là 24h. Mọi giá trị khác đều bị từ
chối với 400 invalid_request và 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 đếm số giá trị linkedId khác nhau mà bạn đã gửi cho thiết bị này — xem
Liên kết tài khoản. botEvents có từ Business trở lên.
Một trường vắng mặt nghĩa là "không có dữ liệu", không bao giờ là số không. Những
trường không có giá trị sẽ bị lược bỏ hoàn toàn thay vì được gửi dưới dạng 0, "" hay
null: một khách truy cập hoàn toàn mới thì không có matchConfidence, một lượt truy cập
sạch thì không có antidetectScore hay suspectScore. Ngoại lệ cố ý duy nhất là
bot.score, luôn hiện diện ngay cả khi bằng không. Hãy đọc các trường một cách phòng thủ.
Payload phụ thuộc vào gói của bạn. Mọi gói có quyền truy cập API đều nhận được phiên
cơ bản — các định danh, dấu thời gian, URL, IP, user agent, trình duyệt, OS, thiết bị,
geo, network, bot, identification và decision. Pro bổ sung
identification.matchType, identification.matchConfidence và bot.antidetectScore.
Business và Enterprise bổ sung geo.isp, network.asn, decision.suspectScore,
identification.driftScore, reasons, behavior, guidance, deviceInfo cùng các
trường ở cấp con người (personId, reputation, linkedAccountsCount,
linkedVisitorsCount). Việc một trường của Business vắng mặt trên gói Pro không phải là
lỗi.
Phần nội bộ ở cấp tín hiệu không bao giờ được trả về, trên bất kỳ gói nào: tên của từng tín hiệu, trọng số của chúng, các ngưỡng đứng sau một phán quyết, giá trị tín hiệu thô và cách phân rã điểm số đều nằm lại ở phía chúng tôi. Một điểm số có thể bị suy ngược ra các đầu vào của nó thì không còn hữu ích như một biện pháp phòng vệ.
Mọi thất bại đều dùng chung một phong bì:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}requestId này không phải là định danh lượt truy cập. Hai giá trị khác nhau dùng
chung một cái tên: bên trong payload của phiên, requestId là UUID của lượt truy cập,
chính là cái mà webhook gửi đến; bên trong phong bì lỗi, nó là một định danh truy vết dài
24 ký tự được sinh ra cho mỗi lệnh gọi HTTP. Định danh truy vết đó cũng quay lại trong
header X-Request-Id trên mọi phản hồi, dù thành công hay không. Hãy đính kèm nó khi bạn
liên hệ bộ phận hỗ trợ — đó là cách chúng tôi tìm ra đúng lệnh gọi của bạn.
| HTTP | code | Ý nghĩa |
|---|---|---|
| 400 | invalid_request | Một tham số bị thiếu hoặc sai định dạng |
| 401 | unauthorized | Khóa vắng mặt, không hợp lệ, bị thu hồi hoặc hết hạn |
| 402 | upgrade_required | Gói của bạn không bao gồm quyền truy cập API |
| 404 | not_found | Không có gì khớp bên trong cửa sổ lưu giữ |
| 405 | method_not_allowed | Route tồn tại, nhưng không dành cho phương thức đó |
| 429 | rate_limited | Vượt số yêu cầu mỗi giây, hoặc vượt hạn mức ngày |
| 500 | internal | Có gì đó hỏng ở phía chúng tôi |
| 503 | unavailable | Một kho dữ liệu nền tạm thời không truy cập được |
Các bước kiểm tra chạy theo một thứ tự cố định — khóa, rồi gói, rồi giới hạn — nên một yêu cầu với khóa sai luôn báo về khóa trước, không bao giờ báo là vấn đề hạn mức.
Hai trường hợp 401 cố ý đọc khác nhau: missing Authorization: Bearer <secret key>
nghĩa là header chưa bao giờ đến, còn invalid or revoked API key nghĩa là nó đã đến
nhưng không khớp. 402 mang theo Data API requires the Pro plan or higher.
Mọi phản hồi đã xác thực đều mang theo tình trạng hiện tại của bạn:
| Header | Ý nghĩa |
|---|---|
X-RateLimit-Limit | Hạn mức ngày của bạn |
X-RateLimit-Remaining | Số lệnh gọi còn lại hôm nay |
X-RateLimit-Reset | Thời gian Unix của lần đặt lại — nửa đêm UTC |
Retry-After | Số giây cần đợi, chỉ gửi kèm 429 |
| Gói | Yêu cầu mỗi giây | Yêu cầu mỗi ngày | Độ sâu lịch sử |
|---|---|---|---|
| Free | Không có quyền truy cập API | — | 7 ngày |
| Pro | 10 | 10.000 | 30 ngày |
| Business | 50 | 100.000 | 90 ngày |
| Enterprise | 200 | Không tính hạn mức | 365 ngày |