Server API는 TRACIO가 귀하의 워크스페이스에 대해 이미 수집한 식별 데이터를 백엔드에서 읽을 수 있게 해줍니다: 방문자의 이력, 개별 세션, 그리고 짧은 구간의 벨로시티 카운터입니다.
이것은 웹훅을 대체하는 것이 아니라 보완합니다:
| 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 | 웹훅 플랫폼 서명용 공개 키(인증 불필요) |
끝의 슬래시는 허용되고 무시됩니다. 알 수 없는 경로나 잘못된 메서드도 다른 모든 경우와 동일한 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 | 요금제, 일수로 표시된 보존 기간, 그리고 실제로 적용된 윈도우 |
보존 기간 안에 데이터가 없는 방문자는 visitor not found in the retention window
메시지와 함께 404 not_found를 반환합니다 — 이는 연동의 오류가 아니라, 그 방문자가
새로운 방문자이거나 보존 기간을 지났다는 뜻입니다.
세션은 두 가지 판정을 담고 있으며, 각각 서로 다른 질문에 답합니다 — 클라이언트가 자동화된 것이었는지, 그리고 위험 엔진이 종합적으로 내린 결론은 무엇인지:
| 필드 | 값 |
|---|---|
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"이 요청은 세션 객체 자체를 반환합니다 — 배열도 아니고 엔벨로프로 감싸지도 않습니다.
윈도우 안에 세션이 없는 방문자에게는 no sessions for this visitor in the retention window
와 함께 404 not_found가 반환됩니다.
또는 웹훅 페이로드에도 등장하는 식별자인 requestId로 조회합니다:
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을 뜻하지 않습니다. 값이 없는
필드는 0, "", null로 보내지 않고 아예 생략됩니다: 완전히 새로운 방문자에게는
matchConfidence가 없고, 깨끗한 방문에는 antidetectScore나 suspectScore가 없습니다.
의도적인 단 하나의 예외는 bot.score로, 값이 0일 때에도 항상 존재합니다. 필드는
방어적으로 읽으세요.
페이로드는 요금제에 따라 달라집니다. API 접근이 포함된 모든 요금제는 기본 세션을
받습니다 — 식별자들, 타임스탬프, URL, IP, user agent, 브라우저, OS, 기기, 위치 정보,
네트워크, 봇, 식별, 판정입니다. 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이며, 웹훅이 전달하는 바로 그
값입니다. 오류 엔벨로프 안에서는 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일 |