Data API(이전에 이 문서에서 Server API로 설명하던 것)는 TRACIO가 귀하의 워크스페이스에 대해 이미 수집한 식별 데이터를 백엔드에서 읽을 수 있게 해줍니다: 방문자의 이력, 개별 세션, 그리고 짧은 구간의 벨로시티 카운터입니다. 몇몇 경로는 쓰기도 합니다 — 계정 레코드, 사용자 정의 필드, 목록 항목 — 귀하의 백엔드가 TRACIO가 알고 있는 정보를 보강할 수 있도록 말입니다.
이것은 웹훅을 대체하는 것이 아니라 보완합니다:
| Webhooks | Data API | |
|---|---|---|
| 방향 | TRACIO가 귀하의 엔드포인트로 푸시 | 귀하의 백엔드가 필요할 때 풀 |
| 시점 | 식별이 일어날 때마다 | 보존 기간 안에서 언제든지 |
| 적합한 용도 | 이벤트에 반응하기 | 판단 도중 데이터 조회, 백필, 조사 |
두 방식 모두 Pro 요금제 이상에서 사용할 수 있습니다.
https://api.tracio.ai/v2이는 브라우저 엔드포인트(edge.tracio.ai)와도, 대시보드(app.tracio.ai)와도 다른
호스트입니다. 셋은 서로 분리되어 있습니다: 브라우저는 공개 키로 엣지와 통신하고,
귀하의 백엔드는 시크릿 키로 Data API와 통신합니다.
/v1 경로는 제거되었습니다. /v2를 사용하세요: 경로는 같고, 모든 JSON 키와 쿼리 파라미터는 snake_case입니다(visitor_id, risk_score, min_risk_score). 귀하의 fields와 labels 사전 안의 키는 귀하의 데이터이므로 원래 표기를 그대로 유지합니다. JavaScript SDK는 camelCase 인터페이스를 유지합니다.
모든 요청은 시크릿 키를 bearer 토큰으로 담아 보냅니다:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Data API는 서버 대 서버 전용입니다. CORS 헤더는 의도적으로 반환되지 않으므로 브라우저에서는 호출할 수 없습니다 — 그것이 시크릿 키를 클라이언트 측 코드 밖에 두게 해 줍니다. 시크릿 키를 브라우저로 내보내지 마세요.
대시보드의 API Keys에서 secret 유형을 선택해 생성하세요.
tracio_sk_ 뒤에 43자가 이어지는 형태로, 총 53자입니다. 대시보드는 키를
앞의 몇 글자로 표시하므로 서로 구분할 수 있습니다.404입니다.교체하면 새 키가 발급되고 이전 키는 7일 동안 계속 동작하므로, 다운타임 없이 배포할 수 있습니다. 새 키를 배포하고, 트래픽이 옮겨 갔는지 확인한 뒤, 이전 키가 만료되도록 두세요. 공개 키는 교체 대상이 아닙니다 — 비밀이 아니며 설계상 페이지 소스에 드러납니다.
| 메서드 | 경로 | 반환값 |
|---|---|---|
GET | /v2/visitors/{visitor_id} | 한 방문자의 집계 이력과 가장 최근 세션 |
GET | /v2/visitors/{visitor_id}/sessions | 해당 방문자의 세션 목록(페이지 단위) |
GET | /v2/visitors/{visitor_id}/sessions/latest | 가장 최근 세션 한 건 |
GET | /v2/visitors/{visitor_id}/velocity | 짧은 윈도우 동안의 활동 카운터 |
GET | /v2/sessions/{request_id} | 요청 식별자로 조회한 세션 한 건 |
POST | /v2/accounts/metadata | 귀하의 계정 ID에 표시 이름과 이메일 첨부 |
POST | /v2/events/fields | 페이지 로드 이후 방문에 사용자 정의 필드 첨부 |
POST | /v2/events/fields/delete | 방문에서 사용자 정의 필드 삭제 — 방문 자체는 건드리지 않음 |
GET | /v2/lists | 워크스페이스 목록: id, 이름, 유형(동작), 모드, 항목 수 |
POST | /v2/lists/{list_id}/entries | 목록에 값 추가, 선택적으로 TTL 지정 |
POST | /v2/lists/{list_id}/entries/delete | 목록에서 값 제거 |
GET | /.well-known/webhook-keys | 웹훅 플랫폼 서명용 공개 키(인증 불필요) |
같은 키와 호스트로 판정 API도 제공되며, 모두 snake_case입니다:
| 메서드 | 경로 | 용도 |
|---|---|---|
POST | /v2/transactions | 비즈니스 이벤트에 대한 동기식 APPROVE / REVIEW / DECLINE 판정 — Business 이상. 단, 제휴 전환(affiliate_id가 있는 account_register 또는 deposit)은 API 접근이 포함된 모든 요금제에서 사용 가능 |
GET | /v2/transactions/{transaction_id} | 레이블과 실효 상태를 포함해 판정을 다시 읽기 — Business 이상. 단, 제휴사에 귀속된 트랜잭션은 제외 |
PUT, DELETE | /v2/transaction-labels | 최대 50건의 트랜잭션에 결과 레이블을 설정하거나 제거 — Business 이상 |
GET | /v2/transaction-labels/value-set | 허용되는 레이블 값과 그 극성 — Business 이상 |
POST | /v2/logins/assess | 로그인 평가: 사유와 함께 allow / challenge / review / deny |
POST, GET | /v2/accounts/{user_id}/trusted-devices | 귀하의 추가 인증(step-up) 후 방문의 기기를 계정의 신뢰 기기로 등록하거나, 계정의 신뢰 기기 목록 조회 |
DELETE | /v2/accounts/{user_id}/trusted-devices/{device_ref} | 기기의 신뢰 철회 |
GET | /v2/affiliates/{affiliate_id} | 90일간의 제휴사 품질 보고서와 hold / review 권고 |
POST, GET | /v2/self-exclusions | 계정에 자기 배제를 설정 또는 연장하거나, 그 목록 조회 |
DELETE | /v2/self-exclusions/{user_id} | 자기 배제를 기한 전에 해제 |
그 결과는 웹훅 이벤트로도 전달됩니다:
transaction_state_changed, login_risk, trusted_device_enrolled,
trusted_device_revoked, affiliate_threshold_crossed.
끝의 슬래시는 허용되고 무시됩니다. 알 수 없는 경로나 잘못된 메서드도 다른 모든 경우와 동일한 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/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"응답은 집계 이력을 담고 가장 최근 세션을 함께 포함하므로, 일반적인 경우 두 번이 아니라 한 번의 요청이면 충분합니다:
{ "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "first_seen_at": "2026-05-02T10:11:12Z", "last_seen_at": "2026-07-25T08:00:00Z", "visits": 42, "incognito_visits": 3, "unique_ips": 5, "unique_countries": 2, "browsers": ["Chrome"], "os": ["macOS"], "devices": ["desktop"], "risk": { "max_risk_score": 63, "avg_bot_score": 12.5, "bot_sessions": 7, "last_decision": "real" }, "network": { "vpn_seen": false, "proxy_seen": false, "tor_seen": false, "datacenter_seen": true, "proxy_detected_seen": true, "last_isp": "Deutsche Telekom", "last_real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" } }, "last_session": { "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "account_id": "user_8842", "account": { "id": "user_8842", "email": "ann@example.com", "display_name": "Ann Lee" }, "timestamp": "2026-07-25T08:00:00Z", "tag": "checkout", "fields": { "order_ref": "A-1029" }, "url": "https://shop.example.com/checkout", "ip": "203.0.113.42", "user_agent": "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, "connection_type": "residential", "proxy_detected": true, "real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" }, "asn": 3320 }, "screen": { "width": 2560, "height": 1600, "color_depth": 30, "pixel_ratio": 2 }, "locale": { "languages": ["en-US", "de"], "timezone": "Europe/Berlin" }, "client_hints": { "architecture": "arm", "bitness": "64", "platform_version": "14.5.0" }, "extensions": [ { "slug": "ublock-origin", "name": "uBlock Origin", "category": "adblock", "risky": false, "store_url": "https://chromewebstore.google.com/detail/cjpalhdlnbpafiamejdnhcphjbkeiagm" } ], "bot": { "result": "human", "score": 4.5, "antidetect_score": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "match_type": "exact", "match_confidence": 0.99 }, "device_info": { "device_id": "d_4f9c2e", "cross_browser": true, "confidence": 0.88, "linked_browsers": 2 }, "decision": { "action": "real", "risk_score": 12, "suspect_score": 3 } }, "meta": { "plan": "business", "retention_days": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| 필드 | 의미 |
|---|---|
visits, incognito_visits | 윈도우 내 총 방문 수와 그중 시크릿 창에서 이루어진 방문 수 |
unique_ips, unique_countries | 윈도우 내에서 확인된 서로 다른 주소와 국가 수 |
browsers, os, devices | 이 방문자가 나타난 서로 다른 환경 |
risk.max_risk_score | 윈도우 내에 기록된 최고 위험 점수, 0..100 |
risk.last_decision | 가장 최근 방문에 기록된 판정 |
risk.avg_bot_score, risk.bot_sessions | 봇 점수 평균과 봇 세션 수 — Business 이상 |
network.*_seen | 이 방문자에게서 VPN, 프록시, Tor 출구 노드 또는 데이터센터 주소가 확인된 적이 있는지 |
network.proxy_detected_seen | 윈도우 안에서 최소 한 번의 방문이 브라우저 앞에 있는 프록시나 VPN을 거쳐 나갔는지 — 기기 사실의 network.proxy_detected 참고 |
network.last_isp | 가장 최근 ISP — Business 이상 |
network.last_real_ip | 프록시나 VPN 뒤에서 관측된 가장 최근 주소(address, country, isp) — Business 이상. 그런 주소를 가져온 방문이 없으면 존재하지 않습니다 |
last_session | 가장 최근 방문의 완전한 세션 객체 |
meta | 요금제, 일수로 표시된 보존 기간, 그리고 실제로 적용된 윈도우 |
보존 기간 안에 데이터가 없는 방문자는 visitor not found in the retention window
메시지와 함께 404 not_found를 반환합니다 — 이는 연동의 오류가 아니라, 그 방문자가
새로운 방문자이거나 보존 기간을 지났다는 뜻입니다.
세션은 두 가지 판정을 담고 있으며, 서로 다른 질문에 답합니다 — 클라이언트가 자동화된 것이었는지, 그리고 위험 엔진이 종합적으로 내린 결론은 무엇인지:
| 필드 | 값 |
|---|---|
bot.result | human, bot, uncertain |
bot.type | bot.result가 bot일 때 존재: 구체적인 도구(playwright, puppeteer, selenium, jsdom, claude_computer_use 등), 검증된 AI 에이전트(ai_agent:<vendor>), 또는 도구를 특정할 수 없을 때의 계열 — automation, headless, antidetect, extension, privacy_browser, impersonation, other |
decision.action | real, fake, suspicious |
bot.score와 decision.risk_score는 둘 다 0..100 범위입니다. Business 이상에서는
guidance가 이를 allow → challenge → review → deny 사다리 위의 시나리오별 권고로
바꿔 줍니다 — 단계별 의미는
Guidance를 참고하세요.
값과 척도는 같은 방문의 v3 웹훅 본문과 일치하며, guidance도 같은 규칙으로 계산됩니다.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&min_risk_score=50"| 파라미터 | 기본값 | 참고 |
|---|---|---|
limit | 50 | 최대 500. 더 큰 값은 거부되지 않고 잘려 맞춰집니다 |
from, to | 요금제 보존 기간 | 위에서 설명한 공통 시간 윈도우 |
cursor | — | 이전 페이지에서 받은 불투명한 페이지네이션 커서 |
bot_result | — | 이 봇 판정을 가진 세션만 남깁니다 |
min_risk_score | — | 이 위험 점수 이상인 세션만 남깁니다, 0..100 |
세션은 최신순으로 돌아옵니다:
{ "items": [ { "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "timestamp": "2026-07-25T08:00:00Z" } ], "next_cursor": "MTcyMTg5NDQwMDAwMDphYmMxMjM", "has_more": true, "meta": { "plan": "business", "retention_days": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}페이지 이동은 커서 기반입니다. page나 offset 파라미터는 없습니다: 받은
next_cursor를 cursor로 다시 넘기고, has_more가 true인 동안 계속 진행하세요.
마지막 페이지에는 next_cursor가 아예 없을 수 있으므로 이 필드를 필수로 두지 마세요.
async function allSessions(visitorId: string, secretKey: string) { const sessions = [] let cursor: string | undefined
do { const url = new URL(`https://api.tracio.ai/v2/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.has_more ? page.next_cursor : undefined } while (cursor)
return sessions}커서는 불투명한 값으로 다루세요 — 그 내용은 구현 세부 사항이며 바뀔 수 있습니다.
수정된 커서는 400 invalid_request와 malformed cursor 메시지로 거부됩니다.
User-Agent에서 가져온 브라우저와 OS와 더불어, 세션은 방문자의 브라우저가 머신에 대해 보고하는 내용을 당사 측에서 정제한 형태로 담습니다. 방문이 그런 데이터를 담지 않았다면 모든 필드는 존재하지 않으므로 각각을 선택적인 것으로 다루십시오.
| 필드 | 의미 |
|---|---|
gpu | 브라우저가 보고하는 비디오 어댑터 모델(WebGL)로, 읽기 쉬운 이름으로 정규화됩니다 — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830. Software renderer는 실제 GPU가 없다는 뜻입니다(가상 머신 또는 headless 환경). Safari는 Apple GPU로 보고합니다 |
network.proxy_detected | 방문의 HTTP 트래픽과 원시 네트워크 경로가 서로 다른 네트워크로 나갑니다 — 브라우저 앞에 프록시나 VPN이 있다는 뜻입니다. 같은 사업자의 주소 두 개(통신사 NAT, 같은 VPN의 두 번째 출구)는 해당하지 않습니다. 주소를 비교할 수 없을 때(iCloud Private Relay, IPv4와 IPv6가 나란히 있는 경우)는 설정되지 않습니다 |
network.real_ip.address, .country, .isp | 원시 네트워크 경로에서 관측된 공인 주소, 즉 프록시나 VPN 뒤에 있는 주소이며 그 국가와 ISP를 함께 제공합니다 — Business 이상. 그런 주소가 관측되지 않으면 존재하지 않습니다(country와 isp는 확인할 수 없으면 존재하지 않습니다) |
device_info.device_id, .cross_browser, .confidence, .linked_browsers | 기기 신원이 확인되었을 때 존재합니다: 그 기기 위의 여러 브라우저를 가로지르는 물리적 기기의 안정적인 id, 이번 방문이 이전과 다른 브라우저로 이루어졌는지, 그 일치의 신뢰도, 그리고 서로 다른 방문자(브라우저) 몇이 이 기기를 공유하는지 — 1을 넘으면 한 대의 머신이 여러 브라우저 신원으로 쓰인다는 뜻입니다 — Business 이상 |
os_environment | 리눅스 머신에서 측정된 데스크톱 환경(Mint 22+, Ubuntu 25.04+, Ubuntu, GNOME, KDE) — Business 이상. 판별되지 않으면 존재하지 않습니다 |
spoofing | 방문이 주장한 바와 독립적인 검사가 측정한 바의 대비(claimed, real, os·gpu·screen·network·browser 중 spoofed_axes, 제품명이 담긴 anonymous_browser) — Business 이상. 위조가 탐지되었을 때만 존재합니다 |
screen.width, .height, .color_depth, .pixel_ratio | 브라우저가 보고하는 화면 해상도, 색 심도, device pixel ratio — Business 이상 |
locale.languages, locale.timezone | 브라우저 자체가 선호하는 언어와 시간대로, IP 주소에서 도출되는 geo.timezone과는 다르며, 둘 사이의 불일치는 위치 위조의 흔한 신호입니다 — Business 이상 |
client_hints.architecture, .bitness, .model, .device_name, .platform_version | User-Agent Client Hints: CPU 아키텍처와 비트 수, 기기 모델 코드(Android, 예: SM-A556B)와 Google Play 기기 목록의 마케팅 이름(device_name, 예: Samsung Galaxy A55 5G), 정확한 플랫폼 버전. Chromium 기반 브라우저만 해당 — Business 이상 |
environment.virtual_machine, environment.hypervisor | 비디오 어댑터가 스스로를 가상이라고 밝힌 경우에만 존재합니다. hypervisor는 닫힌 사전입니다(vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). 블록이 없다는 것은 그런 증거가 없다는 뜻입니다 — Business 이상 |
extensions는 방문 중에 탐지된 브라우저 확장 프로그램을 나열합니다 — Pro 이상입니다.
각 항목은 객체입니다:
| 필드 | 의미 |
|---|---|
slug | 확장 프로그램의 안정적인 기계용 식별자로, 웹훅이 전달하는 값과 동일합니다 |
name | 사람이 읽을 수 있는 이름 |
category | 대략적인 분류 — adblock, privacy, automation, wallet, vpn, devtools, other 등 |
risky | 자동화, 위조, 자격 증명 탈취와 연관된 확장 프로그램에 대해 true |
store_url | 알려진 경우, 확장 프로그램의 스토어 등록 페이지 링크 |
탐지 결과는 저희의 신뢰성 검사를 통과한 뒤에야 보고됩니다 — 모든 탐침에 “설치됨”이라고 답하는 환경이나, 이름이 12개를 넘는 묶음은 신뢰할 수 없는 것으로 보고 버립니다. 따라서 목록이 비어 있거나 없다는 것은 “설치된 확장 프로그램이 없음”이 아니라 “저희가 확인할 수 있었던 것이 없음”을 뜻합니다. 이것을 목록이 아니라 증거로 읽으십시오.
fields에는 연동 측에서 방문에 첨부한 사용자 정의 키-값 쌍이 담깁니다 — 주문 참조 번호, 요금제 이름, 결제 결과 등입니다. 키와 값은 항상 문자열이며, 첨부한 숫자나 불리언 값은 텍스트 형태로 반환됩니다. 세션의 나머지 부분과 마찬가지로 API를 사용할 수 있는 모든 요금제(Pro 이상)에서 제공되며, 방문에 필드가 없으면 포함되지 않습니다.
fields는 방문 시점에 복사되지 않고 모든 요청에서 실시간으로 읽힙니다. 방문 이후에 첨부된 필드(방문 이후 필드 첨부 참고)도 여기에 나타나므로, 같은 방문이라도 fields에는 웹훅이 전달한 것보다 더 많은 항목이 있을 수 있습니다.
이름, 이메일 주소, 전화번호 같은 개인 데이터는 사용자 정의 필드에 넣지 마세요 — 대신 자체 시스템에서 조회할 수 있는 참조 값을 첨부하고, 그 계정이 누구인지는 계정 레코드(아래)로 보내세요.
account는 방문 뒤에 있는 계정에 대한 귀하의 레코드입니다 — 그 account_id에 대해 POST /v2/accounts/metadata로 마지막으로 보낸 내용입니다:
{ "account": { "id": "user-42", "email": "ann@example.com", "display_name": "Ann Lee" }}| 필드 | 설명 |
|---|---|
id | 귀하의 계정 ID — account_id와 같은 값 |
email | 그 계정에 대해 보낸 이메일 주소. 비어 있으면 존재하지 않음 |
display_name | 그 계정에 대해 보낸 표시 이름. 비어 있으면 존재하지 않음 |
레코드는 모든 요청에서 실시간으로 읽히므로, 이름 변경은 과거 방문에도 반영됩니다: 이 블록은 방문 시점이 아니라 현재의 계정 모습을 나타냅니다. API 접근이 포함된 모든 요금제(Pro 이상)에서 제공됩니다. 레코드와 함께 보낸 레이블은 반환되지 않습니다.
방문에 연결된 계정이 없을 때, 그 계정의 레코드가 없을 때(또는 빈 값을 보내 지웠을 때), 그리고 드물게 레코드를 읽지 못했을 때 이 블록은 존재하지 않습니다: 이 경우에도 요청을 실패시키지 않고 세션을 온전히 반환하므로, 블록이 없는 것을 “이 계정에는 이메일이 없음”이 아니라 “알 수 없음”으로 다루세요. 웹훅은 이 블록을 담지 않습니다.
가장 최근 것:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"이 요청은 세션 객체 자체를 반환합니다 — 배열도 아니고 엔벨로프로 감싸지도 않습니다.
윈도우 안에 세션이 없는 방문자에게는 no sessions for this visitor in the retention window
와 함께 404 not_found가 반환됩니다.
또는 웹훅 페이로드에도 등장하는 식별자인 request_id로 조회합니다:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitor_id=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"여기서 visitor_id는 선택 사항이지만, 알고 있다면 함께 넘기는 편이 조회를 눈에 띄게
빠르게 합니다.
identification.drift_score — 계정 탈취 판정의 근거가 된 평활화된 행동 점수로, Business 이상 — 는 이 단일 세션 조회에서만 반환됩니다. 행동 드리프트는 최초 식별 이후 방문 도중에 측정되므로, 목록 경로와 최신 세션 경로에는 포함되지 않습니다. 행동이 한 번도 판정되지 않은 방문에는 drift_score가 없습니다. 같은 값은 account_takeover 웹훅으로도 전달됩니다.
하나의 방문이 웹훅으로 여러 번 전달될 수 있습니다: 먼저 primary, 이어서 느린 신호와 행동 증거가 도착함에 따라 late 또는 correction — 그리고 나중 단계가 앞선 판정을 뒤집을 수 있습니다. Data API는 primary 레코드를 기준으로 답합니다. 나중 단계는 거기에 병합되지 않으므로, 정정된 방문에 대해서는 마지막 웹훅이 더 새로운 답입니다. 방문 몇 분 후에 조치한다면, 여기서 새로 조회하기보다 마지막으로 받은 단계를 우선하세요.
반대 방향으로 작동하는 것이 하나 있습니다: 어떤 방문에서 계정 탈취 탐지기가 발동하면 여기의 guidance는 이를 반영하지만, 같은 방문의 identification 웹훅 본문에는 그 축이 담기지 않습니다 — 그것은 전용 account_takeover 전달로 옵니다.
벨로시티는 "이 방문자가 최근 얼마나 많은 활동을 했는가"에 답합니다 — 크리덴셜 스터핑, 카드 테스팅, 대량 가입에서 나타나는 형태입니다.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v2/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, "unique_ips": 9, "unique_countries": 3, "unique_accounts": 12, "bot_events": 4, "meta": { "plan": "business", "retention_days": 90, "from": "2026-07-25T11:00:00Z", "to": "2026-07-25T12:00:00Z" }}unique_accounts는 이 기기에 대해 귀하가 보낸 서로 다른 linkedId 값의 개수입니다 —
계정 연결을 참고하세요. bot_events는 Business 이상이며, Pro에서는 이 키가 없습니다.
TRACIO는 귀하의 사이트가 linkedId로 넘기는 ID — usr_8412 같은 불투명한 문자열 — 로만 귀하의 계정을 압니다. 사람이 읽을 수 있는 레이블을 한 번 보내면, 그 계정을 보여 주는 모든 대시보드 화면이 원시 ID 대신 이름이나 이메일을 보여 주고, 세션 객체도 이를 account 블록으로 반환합니다. 여기서 보내는 내용은 탐지나 점수 산정에 영향을 주지 않습니다. 이것은 표시용 레이어입니다.
curl -s -X POST "https://api.tracio.ai/v2/accounts/metadata" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"accounts":[ {"account_id":"usr_8412","email":"ann@example.com","display_name":"Ann Lee","labels":{"tier":"vip"}}, {"account_id":"usr_9130","display_name":"Bob Ray"} ]}'{ "updated": 2 }fields가 아닙니다. 여기로 보낸 이메일이나 이름은 귀하의 워크스페이스 계정 레코드에만 저장됩니다: 방문별로 저장되지 않고, 페이지에 쓰이지 않으며, 한 번 보내면 그 계정의 모든 방문이 갱신됩니다.400 invalid_request로 거부되며, 잘못된 항목이 하나만 있어도 배치 전체가 거부됩니다 — 오류는 그 위치를 알려 줍니다. 한 배치 안에서 계정 ID가 중복되는 것은 괜찮습니다: 마지막 것이 적용됩니다.한도: 요청당 1000개 계정, 본문 1 MiB, account_id ≤ 256자, email ≤ 320, display_name ≤ 256, 레이블은 최대 16개이며 키 ≤ 64, 값 ≤ 256자.
브라우저는 렌더링 시점에 페이지가 아는 것만 보낼 수 있습니다 — 주문 ID는 보낼 수 있어도, 결제 확인 결과처럼 나중에 정해지는 것은 보낼 수 없습니다. 이 경로가 그 빈틈을 메웁니다: 방문이 이미 가지고 있는 필드로 방문을 찾아, 서버 측에서 필드를 더 첨부합니다.
curl -s -X POST "https://api.tracio.ai/v2/events/fields" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"match":{"key":"order_ref","value":"A-1029"},"fields":{"amount":"120.00"}}'{ "updated": 1 }match는 방문이 이미 가지고 있는 필드로 방문을 찾습니다. request_id로 찾는 것이 아니며, 범위는 키의 워크스페이스와 환경 안입니다.404 not_found가 반환됩니다 — 결과가 정해지면 곧바로 필드를 쓰세요.updated는 서로 다른 방문의 수를 셉니다. 1을 넘는 것은 match가 둘 이상의 방문에 일치할 때뿐입니다 — 한 주문이나 장바구니에 고유하지 않은 키/값 쌍인 경우입니다. 100개를 넘는 이벤트에 일치하는 match는 임의의 일부에 적용되는 대신 400 invalid_request로 거부됩니다.server 태그와 함께 표시되며, 브라우저가 쓴 같은 키의 값보다 우선합니다.404 not_found입니다. 빈 200이 아닙니다.한도는 브라우저 채널과 같습니다: 호출당 필드 50개, 키 ≤ 40바이트, 값 ≤ 500바이트 — 글자 수가 아니라 바이트 수 — 키 문자 집합 A-Za-z0-9_.:-, 본문 1 MiB.
사용자 정의 필드를 지웁니다 — 방문 자체는 결코 지우지 않습니다. 주문 총액처럼 더 이상 방문에 첨부해 둘 필요가 없는 값에 사용하세요. 이미 전송된 웹훅과 이미 받아 간 내보내기는 바뀌지 않습니다.
# by request_id — clears every field on this one visitcurl -s -X POST "https://api.tracio.ai/v2/events/fields/delete" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"request_id":"9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31"}'
# by match — clears "order_ref" from every visit currently holding "A-1029"curl -s -X POST "https://api.tracio.ai/v2/events/fields/delete" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"match":{"key":"order_ref","value":"A-1029"}}'{ "deleted": 1 }request_id와 match는 상호 배타적입니다 — 둘 다 보내거나 둘 다 보내지 않으면 400 invalid_request입니다. match는 첨부와 같은 24시간 윈도우와 같은 100개 이벤트 상한을 사용합니다.deleted는 방문에서 제거된 필드 수(request_id의 경우) 또는 필드가 제거된 방문 수(match의 경우)를 셉니다.404 not_found가 반환됩니다.운영자가 대시보드에서 관리하는 바로 그 목록에 귀하의 백오피스에서 접근할 수 있습니다. 목록의 유형은 일치하는 방문에 대한 동작입니다 — block, manual_review, challenge, allow — 그리고 모드가 그 동작을 적용할지를 정합니다: observe는 일치를 세기만 하고, enforce는 동작을 guidance(list 축)에 적용합니다. 목록과 그 모드는 대시보드에서 만듭니다. Data API는 목록을 읽고 항목을 추가·제거합니다.
curl -s "https://api.tracio.ai/v2/lists" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX"[ { "id": "7f3a9d2e-4c1b-4e8f-9a6d-1b2c3d4e5f60", "name": "Chargeback accounts", "type": "block", "mode": "enforce", "entries_count": 128 }]# ban an account for 30 dayscurl -s -X POST "https://api.tracio.ai/v2/lists/7f3a9d2e-4c1b-4e8f-9a6d-1b2c3d4e5f60/entries" \ -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ -H "Content-Type: application/json" \ -d '{"value":"usr_8412","value_type":"linked_id","ttl_minutes":43200}'{ "id": "02b4c8d1-6e7f-4a90-b1c2-d3e4f5a6b7c8", "value": "usr_8412", "value_type": "linked_id", "expires_at": "2026-10-19T12:00:00Z", "propagation": "immediate"}항목을 제거하려면 같은 본문으로 POST /v2/lists/{list_id}/entries/delete를 호출하세요. 응답은 { "removed": true, "propagation": "immediate" }입니다.
value_type**은 값을 무엇과 대조할지를 나타냅니다: linked_id, visitor_id, device_id, ip, cidr 또는 country. 그 유형으로 해석되지 않는 값은 400 invalid_request입니다.linked_id → visitor_id → device_id → ip → cidr → country — 그리고 그 안에서는 가장 엄격한 동작이 이깁니다.ttl_minutes는 선택 사항입니다. 생략하면 목록의 기본 TTL이 적용되고, 그것도 없으면 항목은 만료되지 않습니다(expires_at은 null). 항목을 다시 추가하면 갱신됩니다.propagation**은 변경이 언제 적용되는지 알려 줍니다: immediate, 또는 ttl — 약 5분 이내입니다.removed: false와 함께 200입니다. 404는 알 수 없는 목록에만 쓰입니다.필드가 없다는 것은 "데이터 없음"을 뜻하며, 절대 0을 뜻하지 않습니다. 값이 없는
필드는 0, "", null로 보내지 않고 아예 생략됩니다: 완전히 새로운 방문자에게는
match_confidence가 없고, 깨끗한 방문에는 antidetect_score나 suspect_score가 없습니다.
의도적인 단 하나의 예외는 bot.score로, 값이 0일 때에도 항상 존재합니다. 필드는
방어적으로 읽으세요.
페이로드는 요금제에 따라 달라집니다. API 접근은 Pro부터 제공되며, Pro는 기본 세션을 받습니다 — 식별자, 타임스탬프, URL, IP, user agent, 브라우저, OS, 기기, 위치 정보, 네트워크, 봇, 식별, 판정 — 여기에 identification.match_type, identification.match_confidence, bot.score, bot.type, bot.antidetect_score, gpu, network.proxy_detected, extensions, fields, account가 함께 제공됩니다. Business와 Enterprise는 geo.isp, network.asn, network.real_ip, decision.suspect_score, identification.drift_score, reasons, behavior, guidance, device_info, spoofing, os_environment, screen, locale, client_hints, environment를 더합니다. 사람 단위 필드(person_id, reputation, linked_accounts_count, linked_visitors_count)는 Business와 Enterprise를 위해 예약되어 있으며 사람 레이어가 활성화되면 나타납니다 — 오늘은 관찰 모드로 동작하고 있어 이 필드들은 전달되지 않습니다. Pro 요금제에서 Business 필드가 없는 것은 오류가 아닙니다.
신호 수준의 내부 정보는 어떤 요금제에서도 반환되지 않습니다: 개별 신호 이름, 그 가중치, 판정 뒤에 있는 임계값, 원시 신호 값, 점수 구성 내역은 저희 쪽에 남습니다. 입력값으로 역산할 수 있는 점수는 더 이상 방어 수단이 되지 못합니다.
모든 실패는 하나의 엔벨로프를 사용합니다:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "request_id": "8f14e45fceea167a5a36dedd" }}여기서의 request_id는 방문 식별자가 아닙니다. 서로 다른 두 값이 같은 이름을
공유합니다: 세션 페이로드 안에서 request_id는 방문의 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 | 백엔드 저장소에 일시적으로 접근할 수 없습니다 |
검사는 정해진 순서로 실행됩니다 — 키, 그다음 요금제, 그다음 한도 — 따라서 잘못된 키로
보낸 요청은 항상 키 문제를 먼저 알리며, 할당량 문제를 알리는 일은 없습니다. 다른 워크스페이스의 데이터는 존재하지 않는 데이터와 구별되지 않습니다: 403이 아니라 404가 반환됩니다.
두 가지 401은 의도적으로 다르게 읽힙니다: missing Authorization: Bearer <secret key>
는 헤더가 아예 도착하지 않았다는 뜻이고, invalid or revoked API key는 도착했지만
일치하지 않았다는 뜻입니다. 402는 Data API requires the Pro plan or higher를
담습니다.
429와 503은 간격을 점점 늘려 가며 재시도하세요. 그 밖의 4xx는 재시도해도 소용없습니다 — 요청 자체를 고쳐야 한다는 뜻입니다.
키가 인식된 응답에는 현재 상태가 함께 담깁니다(401과 402 응답에는 담기지 않으며, 일일 할당량이 없는 Enterprise에는 할당량 헤더가 붙지 않습니다):
| 헤더 | 의미 |
|---|---|
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일 |