Server API 让您的后端能够读取 TRACIO 已经为您的工作区收集的识别数据:某位访客的 历史记录、单次会话,以及短窗口的活动速率计数器。
它是对 Webhooks 的补充,而不是替代:
| Webhooks | Server API | |
|---|---|---|
| 方向 | TRACIO 推送到您的端点 | 您的后端按需拉取 |
| 时机 | 每次识别发生时 | 在您的保留期内的任意时刻 |
| 适合场景 | 对某个事件作出反应 | 决策过程中查数据、回填、事后调查 |
两种方式都从 Pro 套餐起提供。
https://api.tracio.ai/v1它与浏览器端点(edge.tracio.ai)以及仪表盘(app.tracio.ai)分属不同的主机。三者
彼此独立:浏览器用您的公钥与边缘通信,您的后端用您的私密密钥与 Server API
通信。
每个请求都以 bearer 令牌的形式携带您的私密密钥:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Server API 仅用于服务器之间的调用。 它有意不返回 CORS 头,因此浏览器无法调用它 —— 正是这一点让您的私密密钥远离客户端代码。切勿把私密密钥下发到浏览器。
在仪表盘的 API Keys 中创建,并选择 secret 类型。
tracio_sk_ 后跟 43 个字符,总长 53 个字符。仪表盘按开头的几个字符列出
密钥,方便您区分。轮换会签发一个新密钥,并让旧密钥继续可用 7 天,因此您可以在没有停机的情况下完成 切换。部署新密钥,确认流量已经切过去,然后让旧密钥自然过期。公钥不可轮换 —— 它们不是 机密,按设计就会出现在您的页面源码里。
每个路由都是 GET。Server API 中没有写操作:它只读取数据,您的配置则位于仪表盘中。
| 方法 | 路径 | 返回内容 |
|---|---|---|
GET | /v1/visitors/{visitorId} | 某位访客的聚合历史,以及其最新会话 |
GET | /v1/visitors/{visitorId}/sessions | 该访客会话的分页列表 |
GET | /v1/visitors/{visitorId}/sessions/latest | 仅最近的一次会话 |
GET | /v1/visitors/{visitorId}/velocity | 短窗口内的活动计数器 |
GET | /v1/sessions/{requestId} | 按请求标识符获取的单次会话 |
GET | /.well-known/webhook-keys | 用于 webhook 平台签名的公钥(无需认证) |
末尾的斜杠会被接受并忽略。未知路径或错误的方法同样返回与其他情况一致的 JSON 错误信封, 绝不会返回 HTML 或纯文本页面。
每次读取都受一个时间窗口约束,由两个可选的查询参数控制:
| 参数 | 接受的取值 |
|---|---|
from | YYYY-MM-DD 或完整的 RFC 3339 时间戳 |
to | YYYY-MM-DD 或完整的 RFC 3339 时间戳 |
to 只给日期时,会包含那一整天。400 invalid_request 及消息 time must be YYYY-MM-DD or RFC3339
被拒绝。meta 中回传,因此请检查 meta.from 和 meta.to,不要假定您的请求被原样采纳。curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"响应携带聚合历史,并内嵌最新的一次会话,因此常见场景只需要一次请求而不是两次:
{ "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" }}| 字段 | 含义 |
|---|---|
visits、incognitoVisits | 窗口内的总访问次数,以及其中有多少次发生在隐私窗口中 |
uniqueIps、uniqueCountries | 窗口内出现过的不同地址与国家数量 |
browsers、os、devices | 该访客出现过的不同环境 |
risk.maxRiskScore | 窗口内记录到的最高风险评分,0..100 |
risk.lastDecision | 最近一次访问所记录的判定 |
risk.avgBotScore、risk.botSessions | 机器人评分均值与机器人会话数 —— Business 及以上 |
network.*Seen | 该访客是否曾出现过 VPN、代理、Tor 出口节点或数据中心地址 |
network.lastIsp | 最近一次的 ISP —— Business 及以上 |
lastSession | 最近一次访问的完整会话对象 |
meta | 套餐、以天为单位的保留期,以及实际生效的窗口 |
在保留期内没有任何数据的访客会返回 404 not_found,消息为
visitor not found in the retention window —— 这不是您集成中的错误,它意味着该访客是新
访客,或其数据已超出保留期。
会话携带两种判定,它们回答的是不同的问题 —— 客户端是否为自动化程序,以及风险引擎的 总体结论是什么:
| 字段 | 取值 |
|---|---|
bot.result | human、bot、uncertain |
decision.action | real、fake、suspicious |
bot.score 和 decision.riskScore 的取值范围都是 0..100。在 Business 及以上套餐中,
guidance 会把它们转化为阶梯 allow → challenge → review → deny 上按场景给出的建议
—— 每一级的含义参见Guidance。
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| 参数 | 默认值 | 说明 |
|---|---|---|
limit | 50 | 上限为 500;更大的值会被收窄,而不是被拒绝 |
from、to | 套餐保留期 | 上文所述的共用时间窗口 |
cursor | —— | 上一页返回的不透明分页游标 |
botResult | —— | 只保留具有该机器人判定的会话 |
minRiskScore | —— | 只保留风险评分不低于该值的会话,0..100 |
会话按从新到旧返回:
{ "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" }}翻页基于游标。没有 page 或 offset 参数:把收到的 nextCursor 作为 cursor 传回去,
并在 hasMore 为 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}请把游标当作不透明值 —— 它的内容属于实现细节,可能发生变化。被改动过的游标会以
400 invalid_request 及消息 malformed cursor 被拒绝。
最近的一次:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"它直接返回一个会话对象 —— 不是数组,也没有包在信封里。窗口内没有会话的访客会得到
404 not_found,消息为 no sessions for this visitor in the retention window。
或者按 requestId 获取,这个标识符同样会出现在 webhook 载荷中:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"这里的 visitorId 是可选的,但在您知道它时一并传入会让查询明显更快。
活动速率回答的是“这位访客最近做了多少事” —— 撞库、盗卡试刷和批量注册都是这种形态。
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window 接受 1h、24h 或 7d,默认值为 24h。其他任何取值都会以
400 invalid_request 及 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 统计的是您为该设备发送过的不同 linkedId 取值 ——
参见账户关联。botEvents 为 Business 及以上。
字段缺失表示“没有数据”,而绝不表示零。 没有取值的字段会被整个省略,而不会以 0、
"" 或 null 的形式发送:全新的访客没有 matchConfidence,干净的访问没有
antidetectScore 或 suspectScore。唯一有意的例外是 bot.score,即使为零它也始终存在。
请以防御性的方式读取字段。
载荷取决于您的套餐。 每个具备 API 访问权限的套餐都能拿到基础会话 —— 各类标识符、
时间戳、URL、IP、user agent、浏览器、操作系统、设备、地理位置、网络、机器人、识别与
判定。Pro 增加 identification.matchType、identification.matchConfidence 和
bot.antidetectScore。Business 与 Enterprise 增加 geo.isp、network.asn、
decision.suspectScore、identification.driftScore、reasons、behavior、
guidance、deviceInfo 以及人员级字段(personId、reputation、
linkedAccountsCount、linkedVisitorsCount)。在 Pro 套餐上没有 Business 字段并不是
错误。
任何套餐都不会返回信号级别的内部细节:单个信号的名称、它们的权重、判定背后的阈值、 原始信号取值以及评分构成,都留在我们这一侧。一旦评分可以被反推回它的输入,它作为防护 手段就失效了。
每一次失败都使用同一个信封:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}这里的 requestId 不是访问标识符。两个不同的取值共用了同一个名字:在会话载荷中,
requestId 是该次访问的 UUID,与 webhook 投递的那个相同;而在错误信封中,它是为每次
HTTP 调用生成的 24 字符追踪标识符。无论请求成功与否,该追踪标识符也会通过每个响应的
X-Request-Id 头返回。联系支持时请附上它 —— 我们正是靠它找到您那一次具体的调用。
| HTTP | code | 含义 |
|---|---|---|
| 400 | invalid_request | 缺少某个参数,或参数格式不正确 |
| 401 | unauthorized | 密钥缺失、无效、已吊销或已过期 |
| 402 | upgrade_required | 您的套餐不包含 API 访问权限 |
| 404 | not_found | 保留期内没有任何匹配项 |
| 405 | method_not_allowed | 该路由存在,但不支持这个方法 |
| 429 | rate_limited | 超出了每秒请求数,或每日配额 |
| 500 | internal | 我们这一侧出现了故障 |
| 503 | unavailable | 某个后端存储暂时不可达 |
各项检查按固定顺序执行 —— 先密钥,再套餐,最后限额 —— 因此密钥不正确的请求总是先报告 密钥问题,绝不会报成配额问题。
两种 401 的措辞是有意区分的:missing Authorization: Bearer <secret key> 表示该头
根本没有送达,而 invalid or revoked API key 表示它送达了但没有匹配上。402 携带的是
Data API requires the Pro plan or higher。
每个通过认证的响应都会携带您当前的状态:
| 头 | 含义 |
|---|---|
X-RateLimit-Limit | 您的每日配额 |
X-RateLimit-Remaining | 今天剩余的调用次数 |
X-RateLimit-Reset | 配额重置的 Unix 时间 —— UTC 零点 |
Retry-After | 需要等待的秒数,仅随 429 发送 |
| 套餐 | 每秒请求数 | 每日请求数 | 历史深度 |
|---|---|---|---|
| Free | 不提供 API 访问 | —— | 7 天 |
| Pro | 10 | 10,000 | 30 天 |
| Business | 50 | 100,000 | 90 天 |
| Enterprise | 200 | 不计量 | 365 天 |