Webhooks पहचान इवेंट को रियल-टाइम में आपके सर्वर तक पहुँचाते हैं। हर बार जब कोई
विज़िटर पहचाना जाता है, TRACIO आपके कॉन्फ़िगर किए गए webhook URL पर एक HTTP POST
अनुरोध भेजता है। अनुरोध का बॉडी ही इवेंट पेलोड है।
ये देर से आने वाले वर्डिक्ट देने वाला एकमात्र चैनल भी हैं — यानी वे, जहाँ पेज लोड हो जाने के बाद विज़िटर के व्यवहार ने साबित किया कि वह स्वचालित था।
इन्हें डैशबोर्ड में Settings → Webhooks के अंतर्गत सेट करें। Webhooks के लिए Pro प्लान या उससे ऊपर आवश्यक है।
| इवेंट | कब | प्लान |
|---|---|---|
identification | हर विज़िट पर — primary, late और correction फ़ेज़ | सभी |
account_takeover | किसी खाते के अंतर्गत व्यवहार अब मालिक की प्रोफ़ाइल से मेल नहीं खाता | Business+ |
account_assessment | किसी खाते की अकाउंट-टेकओवर आकलन स्थिति: आकलित और साफ़, आकलित और भिन्न, या आकलित नहीं | Business+ |
attack_detected | आपकी साइट पर बॉट्स का उछाल | Business+ |
reputation_changed | किसी लिंक्ड व्यक्ति की प्रतिष्ठा दूसरे बैंड में चली गई | Business+ |
transaction_state_changed | POST /v2/transactions पर आपके भेजे गए लेनदेन का परिणाम बदल गया | Business+ |
login_risk | POST /v2/logins/assess द्वारा लॉगिन आकलन challenge या review पर समाप्त हुआ | सभी |
trusted_device_enrolled | किसी डिवाइस को किसी खाते के लिए विश्वसनीय बनाया गया, या उसका विश्वास नवीनीकृत हुआ | सभी |
trusted_device_revoked | किसी डिवाइस का विश्वास रद्द किया गया — API के ज़रिए या डैशबोर्ड से | सभी |
affiliate_threshold_crossed | किसी एफ़िलिएट की सिफ़ारिश बदतर हो गई (none → review, none → hold, review → hold) | सभी |
इवेंट के नाम अंडरस्कोर से बनते हैं, कभी डॉट से नहीं — visitor.created या
session.created जैसा कुछ नहीं होता।
एक webhook विशिष्ट प्रकारों की सदस्यता लेता है; अलग मान * का अर्थ है "हर प्रकार,
जिसमें बाद में जोड़े गए भी शामिल हैं"। पाँच प्रकार अपवाद हैं: * में
account_assessment, login_risk, trusted_device_enrolled,
trusted_device_revoked और affiliate_threshold_crossed शामिल नहीं हैं — इनमें से
हर एक का नाम स्पष्ट रूप से दें। account_assessment कोई निष्कर्ष नहीं, बल्कि कवरेज
बताता है, इसलिए यह मोटे तौर पर linked_id वाली हर विज़िट पर एक बार आता है — बाकी
इवेंट से मात्रा का बिल्कुल अलग स्तर। लॉगिन-जोखिम और एफ़िलिएट प्रकार केवल उसी
इंटीग्रेशन के लिए मायने रखते हैं जो उन API को कॉल करता है। इन इवेंट के अस्तित्व से
पहले सेट किया गया वाइल्डकार्ड उन्हें प्राप्त करने की सहमति नहीं है।
सदस्यता बनाते या संपादित करते समय अज्ञात प्रकार को 400 के साथ अस्वीकार किया जाता
है, ताकि किसी टाइपो के कारण आपके पास ऐसा webhook न रह जाए जो चुपचाप कभी ट्रिगर ही न
हो।
identification इवेंट के फ़ेज़एक ही विज़िट से अधिकतम तीन डिलीवरी बनती हैं, जो एक ही request_id साझा करती हैं:
primary — पेज लोड पर आया प्रारंभिक वर्डिक्ट।late — लगभग नौ सेकंड बाद, जब धीमी जांचें पूरी हो जाती हैं, तब का एनरिचमेंट।correction — व्यवहार (पॉइंटर, कीबोर्ड, स्क्रॉलिंग) पर आधारित सुधार।इन्हें request_id से सहसंबंधित करें और phase से अलग पहचानें। बाद वाला फ़ेज़
प्राथमिक होता है: यदि primary ने human कहा और correction कहता है bot, तो सही
उत्तर दूसरा है।
आगमन के क्रम पर निर्भर न रहें। हर फ़ेज़ स्वतंत्र रूप से और अपने अलग रिट्राई शेड्यूल
पर डिलीवर होता है — यदि primary रिट्राई में चला गया जबकि late पहली ही कोशिश में
सफल हो गया, तो आपको वे उल्टे क्रम में मिलेंगे। प्राथमिकता प्राप्ति के समय से नहीं,
phase फ़ील्ड से तय करें।
identification इवेंट के फ़ेज़ बस यही तीन हैं। एक और मान आप तक पहुँचता है:
account_takeover और account_assessment phase: "beacon" लाते हैं, क्योंकि दोनों
सत्र के व्यवहार-बीकन से उठाए जाते हैं।
ध्यान दें कि इससे एक बेमेल बनता है, और यह आइडेम्पोटेंसी पर असर डालता है। प्रोडक्शन में
किसी identification डिलीवरी का event_id ठीक <request_id>:<phase> होता है, पर
अन्य डिलीवरी इस सूत्र को तोड़ती हैं। account_takeover में यह <request_id>:ato होता
है — सफ़िक्स अक्षरशः ato है, phase फ़ील्ड का मान नहीं — और account_assessment
में <request_id>:ato-state-<state>। डैशबोर्ड से भेजी गई टेस्ट डिलीवरी में यह
<request_id>:test होता है, जबकि उसकी बॉडी में phase फिर भी primary दिखाता है — और
स्कीमा 1 बॉडी में phase फ़ील्ड होता ही नहीं, इसलिए वह सफ़िक्स केवल हेडर में ही दिखाई
देता है। event_id को सीधे आइडेम्पोटेंसी कुंजी के रूप में उपयोग करें और उसे कभी
request_id तथा phase से दोबारा न जोड़ें। जिन मानों को आप संभालते हैं उन पर मिलान
करें और बाकी को डिलीवरी अस्वीकार करने के बजाय अनदेखा करें।
कुछ इवेंट की कोई विज़िट नहीं होती: attack_detected (वर्कस्पेस स्तर),
reputation_changed (व्यक्ति स्तर), transaction_state_changed (लेनदेन स्तर),
login_risk, trusted_device_enrolled और trusted_device_revoked (खाता स्तर) तथा
affiliate_threshold_crossed (एफ़िलिएट स्तर)। इनमें न request_id होता है, न
visitor_id, और न ही browser, geo, bot या decision ब्लॉक — ये कुंजियाँ बस
मौजूद नहीं होतीं, और X-Tracio-Request-Id हेडर भी नहीं। इसके विपरीत,
account_takeover और account_assessment किसी विशिष्ट विज़िट से बनते हैं और आपके
प्लान के अनुसार पूरा पहचान बॉडी साथ लाते हैं। यदि आप हर इवेंट एक ही हैंडलर में पार्स
करते हैं, तो विज़िट फ़ील्ड्स को छूने से पहले event जांचें।
नए webhooks पेलोड वर्शन 3 का उपयोग करते हैं। वर्शन 1 या 2 पर मौजूद webhooks अपना चुना हुआ वर्शन बनाए रखते हैं; दोनों पुराने स्कीमा फ़्रीज़ हैं और 2027-04-12 को हटा दिए जाएँगे। माइग्रेट करने के लिए, डैशबोर्ड में webhook कार्ड पर वर्शन 3 चुनें।
| वर्शन | स्थिति |
|---|---|
1 | फ़्रीज़; 2027-04-12 को हटाया जाएगा |
2 | फ़्रीज़; 2027-04-12 को हटाया जाएगा |
3 | नए webhooks के लिए डिफ़ॉल्ट; snake_case कुंजियाँ |
वर्शन 3 वही फ़ील्ड और मान लाता है जो वर्शन 2, बस snake_case कुंजियों और शीर्ष स्तर
पर version: 3 के साथ। सिग्नेचर एल्गोरिद्म और हेडर नहीं बदले
(X-Tracio-Payload-Version: 3)। आपके अपने fields शब्दकोश के भीतर की कुंजियाँ आपका
डेटा हैं और उनका नाम नहीं बदला जाता।
| v2 | v3 |
|---|---|
eventId | event_id |
requestId | request_id |
visitorId | visitor_id |
linkedId | linked_id |
userAgent | user_agent |
network.connectionType | network.connection_type |
network.proxyDetected | network.proxy_detected |
network.realIp | network.real_ip |
bot.antidetectScore | bot.antidetect_score |
identification.matchType | identification.match_type |
identification.matchConfidence | identification.match_confidence |
identification.incognitoVisits | identification.incognito_visits |
identification.driftScore | identification.drift_score |
decision.riskScore | decision.risk_score |
decision.suspectScore | decision.suspect_score |
deviceInfo | device_info |
deviceInfo.deviceId | device_info.device_id |
deviceInfo.crossBrowser | device_info.cross_browser |
deviceInfo.linkedBrowsers | device_info.linked_browsers |
screen.colorDepth | screen.color_depth |
screen.pixelRatio | screen.pixel_ratio |
clientHints | client_hints |
clientHints.platformVersion | client_hints.platform_version |
clientHints.deviceName | client_hints.device_name |
osEnvironment | os_environment |
environment.virtualMachine | environment.virtual_machine |
spoofing.spoofedAxes | spoofing.spoofed_axes |
spoofing.anonymousBrowser | spoofing.anonymous_browser |
session.durationSeconds | session.duration_seconds |
velocity.uniqueIps | velocity.unique_ips |
velocity.uniqueLocations | velocity.unique_locations |
accountAlert | account_alert |
accountAlert.accountId | account_alert.account_id |
accountAlert.driftScore | account_alert.drift_score |
accountAssessment | account_assessment |
accountAssessment.accountId | account_assessment.account_id |
accountAssessment.profile.windowsEffective | account_assessment.profile.windows_effective |
accountAssessment.profile.oldestWindowAgeSeconds | account_assessment.profile.oldest_window_age_seconds |
attack.windowMinutes | attack.window_minutes |
attack.recentBots | attack.recent_bots |
attack.recentTotal | attack.recent_total |
personId | person_id |
reputation.overallRisk | reputation.overall_risk |
linkedAccountsCount | linked_accounts_count |
linkedVisitorsCount | linked_visitors_count |
reputationChange | reputation_change |
reputationChange.personId | reputation_change.person_id |
reputationChange.previousBand | reputation_change.previous_band |
reputationChange.visitorIds | reputation_change.visitor_ids |
reputationChange.linkedAccountsCount | reputation_change.linked_accounts_count |
{ "version": 3, "event": "identification", "event_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31:primary", // "<request_id>:<phase>" — the idempotency key "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", // visit identifier, shared by all phases "phase": "primary", "visitor_id": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "linked_id": "user-42", // your ?lid=, if you passed one "tag": "checkout", "fields": { "order_ref": "A-1029", "plan": "pro" }, // custom fields that arrived with the visit "timestamp": "2026-07-30T12:00:00Z", "url": "https://shop.example.com/checkout", "ip": "203.0.113.44", "user_agent": "Mozilla/5.0 …", "browser": { "name": "Chrome", "version": "138" }, "os": { "name": "macOS", "version": "15.5" }, "device": "desktop", "gpu": "Intel Iris Plus Graphics 655", // वीडियो अडैप्टर का मॉडल, सामान्यीकृत; अज्ञात होने पर अनुपस्थित "geo": { "country": "DE", "city": "Berlin", "lat": 52.52, "lon": 13.405, "timezone": "Europe/Berlin" }, "network": { "vpn": false, "proxy": true, "tor": false, "datacenter": true, "connection_type": "DCH", "proxy_detected": true // HTTP ट्रैफ़िक और कच्चा नेटवर्क पथ अलग-अलग नेटवर्कों से बाहर निकलते हैं }, "bot": { "result": "human", "score": 3.2 }, // human | bot | uncertain "identification": { "confidence": 0.97, "incognito": false }, "decision": { "action": "real", "risk_score": 12.2 }, // real | fake | suspicious "extensions": ["metamask", "privacy_badger"] // detected browser extensions; absent when none}शून्य और खाली मान हटा दिए जाते हैं। शून्य मान वाले स्ट्रिंग और संख्यात्मक फ़ील्ड
(जैसे किसी मानव के लिए bot.type, या bot.antidetect_score: 0) JSON में मौजूद नहीं
होते — उन्हें अपने स्कीमा में अनिवार्य न बनाएं, और नेस्टेड ब्लॉक सावधानी से पढ़ें।
bot.score और decision.risk_score 0..100 स्केल पर दशमलव हैं, दशमलव के बाद एक
अंक के साथ — ठीक वही संख्याएँ जो डैशबोर्ड और Data API उसी विज़िट के लिए दिखाते हैं।
(फ़्रीज़ किए गए v1 स्कीमा में इनकी इकाइयाँ अलग हैं: क्रमशः 0..1 का अंश और 0..255।)
bot.type या तो किसी पहचाने गए बॉट का नाम है, एक सत्यापित AI एजेंट
(ai_agent:<vendor>), या एक परिवार का लेबल। शब्दावली के लिए देखें
बॉट के प्रकार — आंतरिक जांच के नाम किसी भी प्लान पर कभी
उजागर नहीं किए जाते। ai_agent:<vendor> का अर्थ है कि अनुरोध ने साबित किया कि वह उस
वेंडर के इन्फ्रास्ट्रक्चर से आया है — वेंडर द्वारा प्रकाशित कुंजी से बना वैध Web Bot
Auth सिग्नेचर, या वेंडर द्वारा प्रकाशित IP रेंज के भीतर का पता — न कि केवल user agent
में ऐसा दावा किया। जो अनुरोध AI एजेंट होने का दावा करता है और जिसका खंडन वेंडर द्वारा
प्रकाशित हर प्रमाण करता है, उसे impersonation के रूप में रिपोर्ट किया जाता है।
| फ़ील्ड | प्रकार | विवरण |
|---|---|---|
version | number | पेलोड स्कीमा वर्शन (3) |
event | string | इवेंट प्रकार |
event_id | string | डिलीवरी पहचानकर्ता — आइडेम्पोटेंसी कुंजी; प्रति इवेंट अद्वितीय और आपके उन सभी webhooks में साझा जो इसे प्राप्त करते हैं, रिट्राई में स्थिर |
request_id | string | विज़िट पहचानकर्ता (UUID), विज़िट के सभी फ़ेज़ों में साझा |
phase | string | primary, late, correction; account_takeover और account_assessment beacon लाते हैं |
visitor_id | string | स्थिर विज़िटर पहचानकर्ता |
linked_id | string | क्लाइंट द्वारा दिया गया लिंक्ड पहचानकर्ता |
tag | string | क्लाइंट द्वारा दिया गया कस्टम टैग |
fields | object | संग्रह के समय विज़िट से जुड़े कस्टम फ़ील्ड। केवल वही जो विज़िट के साथ आया — Data API के ज़रिए बाद में जोड़ा गया फ़ील्ड webhook में कभी नहीं दिखता, किसी भी फ़ेज़ या रिट्राई पर। कुंजियाँ और मान स्ट्रिंग हैं। विज़िट में कोई फ़ील्ड न हो तो अनुपस्थित |
timestamp | string | इवेंट का समय (RFC 3339) |
url | string | वह पेज URL जहाँ इवेंट कैप्चर हुआ |
ip | string | क्लाइंट का IP पता |
user_agent | string | कच्ची क्लाइंट user-agent स्ट्रिंग |
browser.name / .version | string | पहचाना गया ब्राउज़र |
os.name / .version | string | पहचाना गया ऑपरेटिंग सिस्टम |
device | string | डिवाइस श्रेणी (जैसे desktop, mobile) |
gpu | string | ब्राउज़र द्वारा बताया गया वीडियो अडैप्टर का मॉडल (WebGL), पढ़ने योग्य नाम में सामान्यीकृत (Intel Iris Xe Graphics, Apple M1 Pro); सॉफ़्टवेयर रैस्टराइज़र के लिए Software renderer; अज्ञात होने पर अनुपस्थित |
geo | object | IP जियोलोकेशन: country, city, lat, lon, timezone |
network | object | vpn, proxy, tor, datacenter (बूलियन) और connection_type |
network.proxy_detected | boolean | विज़िट का HTTP ट्रैफ़िक और उसके कच्चे नेटवर्क पथ अलग-अलग नेटवर्कों से बाहर निकलते हैं — यानी ब्राउज़र के आगे कोई प्रॉक्सी या VPN है। एक ही प्रदाता के दो पते (कैरियर NAT, उसी VPN का दूसरा निकास) नहीं गिने जाते |
bot.result | string | human, bot या uncertain |
bot.type | string | बॉट मिलने पर बॉट का नाम, सत्यापित AI एजेंट या परिवार का लेबल |
bot.score | number | बॉट स्कोर (0–100) |
identification.confidence | number | पहचान कॉन्फ़िडेंस (0.0–1.0) |
identification.incognito | boolean | निजी/इनकॉग्निटो ब्राउज़िंग संदर्भ |
decision.action | string | real, fake या suspicious |
decision.risk_score | number | समग्र जोखिम स्कोर (0–100) |
extensions | array | इस विज़िट में पहचाने गए ब्राउज़र एक्सटेंशन, स्थिर मशीन स्लग के रूप में (नाम, श्रेणियाँ और स्टोर लिंक डैशबोर्ड और Data API में हैं)। कुछ न मिले तो अनुपस्थित |
Pro और उससे ऊपर — विज़िटर समय के साथ कैसा व्यवहार करता है:
{ "identification": { "match_type": "exact", // exact | fuzzy | new — how the visitor was recognized "match_confidence": 0.93, "visits": 42, "incognito_visits": 3 }, // Present when visitor counters are available at event time (usually primary). // A missing block means "no data", not "zeros". "velocity": { "events5m": 7, "unique_ips": 2, "unique_locations": 1 }, "bot": { "antidetect_score": 0 }, // antidetect indicators, 0..100 "session": { "duration_seconds": 95 } // the correction phase and the account_takeover alert}Business और उससे ऊपर — वर्डिक्ट वैसा क्यों निकला:
{ "reasons": [ // at most 8, sorted by importance { "code": "headless_browser", "severity": "high" }, { "code": "privacy_hardening", "severity": "low" } ], // Behavioral biometrics — present only when behavioral scoring ran for the // visit. A missing block means "no data", never "nothing suspicious". "behavior": { "score": 87, "verdict": "human", "confidence": 0.92 }, // The smoothed behavior score the account-takeover verdict was made on. It is // measured by beacons that arrive after the identification event, so it is // empty in identification deliveries — see the account_takeover event. "identification": { "drift_score": 0.41 }, "device_info": { "device_id": "…", // the physical device across browsers on it "cross_browser": true, "confidence": 0.88, "linked_browsers": 3 }, "network": { "isp": "Deutsche Telekom", "asn": 3320, // कच्चे नेटवर्क पथ पर देखा गया सार्वजनिक पता, यानी प्रॉक्सी या VPN के पीछे का // पता; ऐसा कोई पता न दिखे तो अनुपस्थित "real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" } }, "geo": { "isp": "Deutsche Telekom" }, // Linux मशीन पर वास्तव में मापा गया डेस्कटॉप एनवायरनमेंट ("Mint 22+", // "Ubuntu", "GNOME", "KDE"); User-Agent किसी डिस्ट्रीब्यूशन को व्यक्त नहीं कर // सकता। निर्धारित न होने पर अनुपस्थित — अधिकांश Linux विज़िट में, और हर // ग़ैर-Linux विज़िट में। "os_environment": "Mint 22+", // विज़िट ने अपने बारे में क्या दावा किया, बनाम स्वतंत्र जाँचों ने क्या मापा। // केवल तभी मौजूद जब स्पूफ़िंग वाकई पकड़ी गई हो; ख़ाली `real` फ़ील्ड का अर्थ है // "जाँच चुप रही", कभी "पुष्ट" नहीं। `gpu` अक्ष पर `claimed.gpu` दावा किए गए // अडैप्टर को उसी पढ़ने योग्य मॉडल नाम से रखता है जो शीर्ष स्तर के `gpu` फ़ील्ड // में है। "spoofing": { "detected": true, "claimed": { "os": "Windows 10", "browser": "Chrome 139.0" }, "real": { "os": "macOS" }, "spoofed_axes": ["os", "screen"], // os | gpu | screen | network | browser "anonymous_browser": { "detected": true, "names": ["Linken Sphere"] } }, // डिवाइस के तथ्य — विज़िटर का ब्राउज़र मशीन के बारे में क्या बताता है, हमारी ओर // से साफ़ किया हुआ। screen: रिज़ॉल्यूशन, कलर डेप्थ और device pixel ratio। // locale: ब्राउज़र की अपनी पसंदीदा भाषाएँ और उसका टाइमज़ोन — geo.timezone के // विपरीत, जो IP पते से निकाला जाता है; दोनों में बेमेल नकली लोकेशन का आम // संकेत है। client_hints: User-Agent Client Hints — CPU की आर्किटेक्चर और // बिटनेस, डिवाइस मॉडल (Android: `model` में मॉडल कोड, जैसे "SM-A556B", और // Google Play डिवाइस सूची से उसका मार्केटिंग नाम `device_name` में, जैसे // "Samsung Galaxy A55 5G") और प्लेटफ़ॉर्म का सटीक संस्करण; इन्हें केवल // Chromium आधारित ब्राउज़र बताते हैं। विज़िट ऐसा डेटा न लाए तो हर ब्लॉक // अनुपस्थित रहता है, इसलिए हर एक को वैकल्पिक मानें। "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": "15.5.0" }, // केवल तभी मौजूद जब वीडियो अडैप्टर ने ख़ुद को वर्चुअल बताया हो; hypervisor एक // बंद शब्दकोश है (vmware, virtualbox, parallels, qemu, hyperv, bochs, // intel-gvt, vgpu)। ब्लॉक का न होना यह बताता है कि ऐसा कोई प्रमाण नहीं है। "environment": { "virtual_machine": true, "hypervisor": "vmware" }, "decision": { "suspect_score": 4.1 }, "guidance": { "version": 3, "overall": "review" } // see below}रीज़न-कोड शब्दावली और severity का अर्थ जानने के लिए देखें
बॉट डिटेक्शन।
मल्टी-अकाउंटिंग फ़ील्ड (Business और उससे ऊपर)। आपके वर्कस्पेस के लिए पर्सन मोड
सक्षम होने पर बॉडी में person_id, एक reputation ब्लॉक (score, categories,
overall_risk), linked_accounts_count और linked_visitors_count भी आते हैं — यानी
विज़िटर अन्य विज़िटरों और खातों से जुड़ा है या नहीं। पर्सन लेयर फ़िलहाल अवलोकन मोड में
चलती है, और उसके लाइव होने तक ये फ़ील्ड बॉडी में अनुपस्थित रहते हैं। इन्हें अपने स्कीमा
में अनिवार्य न बनाएं।
guidance हर इंटीग्रेशन बिंदु के लिए "क्या करें" की तैयार सिफ़ारिशें लाता है, ताकि
आपको कच्चे स्कोर से नीति निकालनी न पड़े। यह हर v3 Business+ इवेंट में मौजूद होता है —
allow स्पष्ट रूप से बताया जाता है:
{ "guidance": { "version": 3, "overall": "review", // the strictest advice across the scenarios "payment": "review", // whether to accept the payment "registration": "challenge", // whether to create the account "login": "challenge", // whether to let them into the account "affiliate": "review", // whether to credit the conversion to the partner "basis": ["risk", "network"] // the axes that determined the advice }}हर परिदृश्य allow से शुरू होता है और सीढ़ी पर केवल ऊपर ही जाता है:
allow → challenge → review → deny। किसी परिदृश्य के भीतर, ट्रिगर हुआ सबसे सख़्त
अक्ष जीतता है, और overall सभी चार परिदृश्यों में सबसे सख़्त होता है।
| सलाह | भुगतान | पंजीकरण | लॉगिन | एफ़िलिएट |
|---|---|---|---|---|
allow | इसे प्रोसेस करें | इसे बनाएं | उन्हें अंदर आने दें | कन्वर्ज़न का श्रेय दें |
challenge | 3-D Secure / पुष्टि | कैप्चा, ईमेल या फ़ोन पुष्टि | स्टेप-अप 2FA, दोबारा प्रमाणीकरण | गतिविधि दिखने तक संदिग्ध के रूप में चिह्नित करें |
review | प्रोसेस करें, पर समीक्षा में लगाएं | प्रतिबंधों के साथ बनाएं | अंदर आने दें, पर अलर्ट उठाएं | समीक्षा होने तक भुगतान रोकें |
deny | लेनदेन प्रोसेस न करें | खाता बनाने से मना करें | उन्हें अंदर न आने दें | कन्वर्ज़न का श्रेय न दें |
basis उन अक्षों की सूची है जिन्होंने सलाह तय की — जिस अक्ष का प्रस्ताव अपनाई गई
सलाह से नरम था, वह सूची में नहीं आता। इसे किसी निश्चित सेट को मानकर नहीं, बल्कि कोड के
अनुसार पढ़ें:
अक्ष (basis) | अर्थ |
|---|---|
list | विज़िटर enforce मोड में आपकी किसी वर्कस्पेस सूची से मेल खाया। allow सूची हर अक्ष को ओवरराइड करती है: हर परिदृश्य allow हो जाता है और basis ठीक ["list"] होता है |
rule | विज़िट enforce मोड में आपके किसी वर्कस्पेस नियम से मेल खाई; नियम केवल अपने परिदृश्य की सलाह बदलता है (allow नियम केवल उसी को नरम करता है), और जब कोई सूची पहले ही लागू हो चुकी हो तो छोड़ दिया जाता है |
bot | बॉट डिटेक्टर ने निष्कर्ष निकाला कि यह ऑटोमेशन है — हर परिदृश्य को अस्वीकार करता है |
risk | विज़िट का निर्णय और समग्र जोखिम स्कोर |
behavior | व्यवहारिक बायोमेट्रिक्स पर्याप्त कॉन्फ़िडेंस के साथ बॉट बताते हैं — अस्वीकार के बजाय घर्षण जोड़ता है |
account_takeover | इस खाते के अंतर्गत व्यवहार अब उसके मालिक की प्रोफ़ाइल से मेल नहीं खाता — लॉगिन को challenge, भुगतान को review |
network | Tor, या डेटासेंटर से निकलने वाला VPN |
affiliate_pattern | स्व-निर्मित रेफ़रल का पैटर्न — एफ़िलिएट कन्वर्ज़न challenge के रूप में रोका जाता है |
fraud_reputation, multiaccounting | विज़िटर के पीछे के व्यक्ति की प्रतिष्ठा और खातों का क्लस्टरिंग; पर्सन लेयर लाइव होने पर दिखते हैं |
version नियम-सेट का वर्शन है — लॉजिक बेहतर होने पर इसे बढ़ाया जाता है, इसलिए इसे अपने
लॉग में रखें। वर्शन 2 ने list अक्ष जोड़ा, जो सलाह को नरम कर सकने वाला पहला अक्ष है;
वर्शन 3 ने account_takeover के लॉगिन थ्रेशोल्ड को अकाउंट-टेकओवर वर्डिक्ट स्कोर के
स्केल पर ले आया, इसलिए उसी विज़िट को deny मिल सकता है जहाँ वर्शन 2 ने challenge
दिया था, और उल्टा भी।
गाइडेंस योगात्मक है: नए परिदृश्य नई कुंजियों के रूप में आते हैं और अनुबंध नहीं तोड़ते।
बाद वाला फ़ेज़ जीतता है, सिवाय आंशिक सलाह के: अधूरे इनपुट सेट पर गणना की गई
डिलीवरी "partial": true से चिह्नित होती है, और आंशिक सलाह उसी request_id के लिए
पहले प्राप्त पूर्ण सलाह को नहीं बदलती। सामान्य डिलीवरी में partial फ़ील्ड पूरी
तरह अनुपस्थित रहता है।
सटीक थ्रेशोल्ड जान-बूझकर दस्तावेज़ में नहीं दिए गए हैं। जिस सलाह से स्कोर की रिवर्स
इंजीनियरिंग हो सके, वह सुरक्षा नहीं रह जाती। ठीक एक जान-बूझकर अपवाद है:
account_assessment.verdict.threshold, नीचे वर्णित अकाउंट-टेकओवर वर्डिक्ट की निर्णय
सीमा।
account_takeover इवेंटकेवल Business और Enterprise; निचले प्लान पर कोई डिलीवरी नहीं बनती, भले ही कोई webhook
इस इवेंट की सदस्यता ले चुका हो। बॉडी आपके प्लान के अनुसार पूरा पहचान एनवेलप है, साथ में
एक account_alert ब्लॉक और अगले खंड में वर्णित account_assessment ब्लॉक, जो प्रति
विज़िट अधिकतम एक बार भेजा जाता है:
{ "version": 3, "event": "account_takeover", "event_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31:ato", "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "phase": "beacon", // अलर्ट बीकन से उठाया जाता है; केवल event_id "ato" कहता है "account_alert": { "type": "behavior-drift/2", // kind of alert and the version of its construction "account_id": "user-42", // your linked_id for the account "drift_score": 0.71 // the value the verdict was made on }, "account_assessment": { "type": "behavior-drift/2", "account_id": "user-42", "state": "assessed_alert", "verdict": { "severity": "high", "score": 0.71, "threshold": 0.3565, "scale": "manhattan-normalized", "basis": "ring", "windows": 9 }, "profile": { "windows": 6, "windows_effective": 5, "oldest_window_age_seconds": 1837 } } // …the remaining identification fields}type में वर्शन क्यों है। स्कोर के पीछे की संरचना पहले भी बदल चुकी है: वर्डिक्ट का
स्केल और आधार। behavior-drift/2 पहली वर्शन वाली संरचना है। यदि आपने इस संख्या पर
अपने नियम कैलिब्रेट किए हैं, तो इस मान के बदलने को उन्हें दोबारा जांचने का संकेत मानें।
account_alert.drift_score और identification.drift_score एक ही संख्या हैं
(2026-10-03 से): वह स्मूद किया गया व्यवहार स्कोर जिस पर वर्डिक्ट दिया गया, खाते के
मालिक की पिछली विश्वसनीय विंडो की रिंग के विरुद्ध मापा गया। उस तारीख से पहले
identification.drift_score एक अलग स्केल पर कच्ची दूरी लाता था — उस तारीख के आर-पार
मानों को न मिलाएं। जब किसी विंडो का आकलन नहीं हुआ, तो फ़ील्ड शून्य के बजाय अनुपस्थित
होता है। जब आपको संख्या उस थ्रेशोल्ड के साथ चाहिए जिससे उसकी तुलना हुई, तो
account_assessment.verdict उपयोग करें।
v1 में ब्लॉक accountAlert है, जिसमें type, linkedId और drift हैं, और कोई
आकलन ब्लॉक नहीं है। v2 में अलर्ट दो फ़ील्ड का नाम बदलता है — linkedId → accountId
और drift → driftScore — और accountAssessment जोड़ता है; v3 ऊपर दिए snake_case
नाम उपयोग करता है। वर्शन बदलते समय अपना हैंडलर अपडेट करें, वरना आपका अकाउंट-टेकओवर
लॉजिक चुपचाप डेटा देखना बंद कर देगा।
account_assessment इवेंटकेवल Business और Enterprise। स्पष्ट सदस्यता आवश्यक है: * में यह शामिल नहीं है।
इस इवेंट के बिना, अलर्ट न होने के एक साथ दो अलग अर्थ होते हैं: या तो हमने सत्र को खाते के मालिक की प्रोफ़ाइल के विरुद्ध आकलित किया और वह मेल खाया, या हम उसे आकलित ही नहीं कर सके — मालिक की प्रोफ़ाइल तैयार नहीं है, विंडो बहुत विरल थी, या ट्रैफ़िक किसी व्यक्ति का नहीं था। दोनों चुप्पी जैसे दिखते हैं, और चुप्पी सुरक्षा जैसी पढ़ी जाती है। अक्सर ऐसा नहीं होता: सितंबर 2026 के एक पूरे प्रोडक्शन दिन में हमने 73% विंडो और 80% खातों पर वर्डिक्ट देने से परहेज़ किया। "अलर्ट नहीं, तो ठीक है" पर बनी प्रक्रिया उस स्थिति पर बनी है जो अधिकतर हमारे पास होती ही नहीं।
{ "version": 3, "event": "account_assessment", "event_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31:ato-state-not_assessed", "request_id": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "phase": "beacon", "account_assessment": { "type": "behavior-drift/2", "account_id": "user-42", "state": "not_assessed", // always present, one of three values "reason": "profile_not_ready", // only when state is not_assessed "profile": { "windows": 3, // windows in the owner's reference ring "windows_effective": 2, // of those, how many yielded a distance "oldest_window_age_seconds": 1837 } } // …the remaining identification fields}state कभी अनुपस्थित नहीं होता और कभी खाली नहीं होता:
state | अर्थ | क्या करें |
|---|---|---|
assessed_clear | आकलित; व्यवहार मालिक से मेल खाता है | कुछ नहीं |
assessed_alert | आकलित; व्यवहार भिन्न है | स्टेप-अप — account_takeover अलर्ट वही वर्डिक्ट लाता है |
not_assessed | कोई वर्डिक्ट नहीं दिया गया | इसे साफ़ न मानें; अपने संकेतों पर लौटें |
reason (केवल not_assessed के साथ):
reason | अर्थ |
|---|---|
profile_not_ready | मालिक का संदर्भ फ़िलहाल इस खाते के लिए वर्डिक्ट नहीं दे सकता। इसे एक चरण नहीं, एक स्थिति मानें: यह शायद कभी न बदले, इसलिए प्रतीक्षा न करें |
profile_learning | संदर्भ अभी जमा हो रहा है |
window_too_sparse | कोई व्यक्ति मौजूद था, पर विंडो में बहुत कम इंटरैक्शन था |
not_computable | विंडो और संदर्भ में कोई साझा सूचनात्मक अक्ष नहीं था |
bot_traffic | पर्याप्त डेटा था, और आकलन के लिए कोई व्यक्ति नहीं था |
2026-10-04 18:00 UTC से, जिस मालिक का संदर्भ तैयार है उसके लिए window_too_sparse और
not_computable अब जारी नहीं किए जाते — ऐसी विंडो profile_not_ready के रूप में
रिपोर्ट होती है। ये दो कारण सूची में बने हुए हैं क्योंकि पहले की डिलीवरी इन्हें लाती
हैं: यदि आपका हैंडलर इन पर शाखा बनाता है, तो profile_not_ready को भी उसी तरह भेजें,
और उस तारीख के आर-पार इन कारणों की दरों की तुलना न करें।
state और reason प्लान से बंधे नहीं हैं। verdict Business+ है, drift_score की
तरह।
verdict ब्लॉक केवल assessed_clear और assessed_alert के साथ मौजूद होता है।
not_assessed के साथ ब्लॉक शून्य से भरा होने के बजाय अनुपस्थित होता है — 0 के threshold
के बगल में 0 का score "व्यवहार बिल्कुल मेल खाया" जैसा पढ़ा जाएगा।
| फ़ील्ड | अर्थ |
|---|---|
severity | low या high — जान-बूझकर दो बैंड: बीच के बैंड के लिए उसी स्केल पर दूसरा थ्रेशोल्ड चाहिए होता |
score | वह मान जिस पर वर्डिक्ट दिया गया |
threshold | वह मान जिससे इसकी वास्तव में तुलना की गई |
scale | स्कोर कैसे बनता है: manhattan-normalized एक सामान्यीकृत व्यवहारिक स्पेस में d/(1+d) है, जहाँ 0 का अर्थ ठीक मिलान है |
basis | ring — मालिक की पिछली विश्वसनीय विंडो में से निकटतम तक दूरी; centroid — उसकी औसत प्रोफ़ाइल तक दूरी |
windows | स्कोर में कितनी विंडो एकत्रित की गईं |
score और threshold हमेशा एक ही, नामित स्केल पर होते हैं, इसलिए यह जोड़ा निर्णय को
दोहराता है: अलर्ट ठीक तब ट्रिगर होता है जब score > threshold।
यह कितनी बार आता है। प्रति विज़िट प्रति स्थिति अधिकतम एक डिलीवरी — व्यवहार में,
linked_id वाली प्रति विज़िट एक, प्रति व्यवहारिक विंडो एक नहीं। एक ही विज़िट के भीतर
स्थिति बदल सकती है — उदाहरण के लिए, संदर्भ सीखना पूरा कर लेता है और not_assessed
assessed_clear बन जाता है — और तब आपको दोनों मिलते हैं, क्रम में। यह संक्रमण संभव है,
वादा नहीं: profile_not_ready खाते के पूरे जीवनकाल तक बना रह सकता है।
यह क्या वादा नहीं करता। सबसे अच्छी सुरक्षा लंबे सत्रों और पूर्ण संदर्भ प्रोफ़ाइल वाले सक्रिय उपयोगकर्ताओं के खातों की होती है; सबसे कमज़ोर सुरक्षा कभी-कभार आने वाले उपयोगकर्ताओं की, जिनके लिए टेकओवर अधिक ख़तरनाक है। यह संरचना का गुण है, कोई थ्रेशोल्ड नहीं जिसे ट्यून किया जा सके। इवेंट किसी संदर्भ से व्यवहारिक विचलन की रिपोर्ट करता है — यह सामान्य रूप से अकाउंट टेकओवर पकड़ने का दावा नहीं है।
attack_detected इवेंटबॉट-उछाल का अलर्ट साझा v3 एनवेलप में attack ब्लॉक के साथ आता है। इसमें पहचान फ़ील्ड
नहीं होते — यह वर्कस्पेस-स्तर का इवेंट है:
{ "version": 3, "event": "attack_detected", "event_id": "c0a8e1f2-…", // alert id; matches X-Tracio-Event-Id "timestamp": "2026-07-30T12:00:00Z", "attack": { "kind": "bot_spike", "severity": "critical", // warning | critical "window_minutes": 15, "recent_bots": 4210, "recent_total": 5100, "expected": 180.5 // the baseline expected over a window this size }}पेलोड वर्शन 1 वाले webhooks को ऐतिहासिक फ़्लैट अलर्ट स्कीमा मिलता है — वह नहीं बदलता।
login_risk, trusted_device_enrolled और trusted_device_revoked इवेंटये तीन इवेंट लॉगिन जोखिम API (POST /v2/logins/assess) और उसके विश्वसनीय डिवाइस पर
आपकी अपनी कॉल से आते हैं। ये केवल नाम से स्पष्ट सदस्यता पर डिलीवर होते हैं — * में
ये शामिल नहीं हैं — और केवल पेलोड वर्शन 2 या 3 वाले webhooks को; वर्शन 1 में इनके लिए
कोई बॉडी नहीं है।
login_risk तब भेजा जाता है जब आकलन challenge या review लौटाता है — allow,
deny या not_assessed पर कभी नहीं। इसका login_risk ब्लॉक संग्रहीत आकलन है, ठीक
वैसा जैसा सिंक्रोनस उत्तर ने दिया था। इवेंट id: login-risk:<id>, जहाँ <id>
संग्रहीत रिकॉर्ड का हमारा पहचानकर्ता है — अपना assessment_id बॉडी से लें।trusted_device_enrolled और trusted_device_revoked किसी विश्वसनीय डिवाइस के हर
नामांकन, नवीनीकरण और रद्दीकरण पर भेजे जाते हैं। इनका trusted_device ब्लॉक —
user_id, device_ref, enrolled_at, expires_at, revoked_at, revoked_by
(api या dashboard) — इवेंट के क्षण का स्नैपशॉट है; trusted_device_enrolled के
साथ revoked_at और revoked_by null होते हैं। इवेंट id: trusted-device:<n>।ये अतुल्यकालिक रूप से, कॉल के लगभग पाँच सेकंड बाद तक भेजे जाते हैं, और इनमें
X-Tracio-Request-Id हेडर नहीं होता — विज़िट खोजने के लिए login_risk.request_id उपयोग
करें। webhook ऑडिट और अलर्टिंग के लिए एक सूचना है; निर्णय स्वयं सिंक्रोनस उत्तर है।
{ "version": 3, "event": "login_risk", "event_id": "login-risk:4b1f9e2c-6d3a-4f80-9b7e-2a5c8d1e0f37", "timestamp": "2026-10-17T10:00:00Z", "login_risk": { "assessment_id": "snap-1", "user_id": "alice", "state": "assessed", "recommendation": "challenge", "reasons": ["new_device"] // …the rest of the assessment }}{ "version": 3, "event": "trusted_device_revoked", "event_id": "trusted-device:7", "timestamp": "2026-10-17T11:00:00Z", "trusted_device": { "user_id": "alice", "device_ref": "7978151c54e94838", "enrolled_at": "2026-10-17T10:00:00Z", "expires_at": "2026-11-16T10:00:00Z", "revoked_at": "2026-10-17T11:00:00Z", "revoked_by": "dashboard" }}affiliate_threshold_crossed इवेंटकेवल नाम से स्पष्ट सदस्यता पर डिलीवर होता है — * में यह शामिल नहीं है — और केवल
पेलोड वर्शन 2 या 3 वाले webhooks को। हर 15 मिनट में TRACIO पर्याप्त डेटा वाले प्रत्येक
एफ़िलिएट का क्वालिटी स्कोर दोबारा गणना करता है और जब उसकी सिफ़ारिश बदतर होती है तब इवेंट
भेजता है: none → review, none → hold या review → hold। सुधार होने पर कुछ नहीं भेजा
जाता, और अगली गिरावट फिर से रिपोर्ट हो सकती है। इवेंट वर्कस्पेस का है: यह हर एनवायरनमेंट
के सदस्यता वाले webhooks को जाता है और इसमें X-Tracio-Request-Id हेडर नहीं होता। इवेंट
id: affiliate-threshold:<affiliate_id>:<recommendation>:<unix time>।
{ "version": 3, "event": "affiliate_threshold_crossed", "event_id": "affiliate-threshold:aff_17:review:1792490400", "timestamp": "2026-10-20T10:00:00Z", "affiliate_threshold": { "affiliate_id": "aff_17", "recommendation": "review", // none | review | hold "previous_recommendation": "none", "quality_score": 72, // 0..100, higher is better "hold_days": 14, // the current hold period of the workspace "crossed_at": "2026-10-20T10:00:00Z", "metrics": { "reg_to_ftd": 0.31, "active14_share": 0.42, "dup_device_share": 0.3, "dup_person_share": 0.1, "ring_share": 0.05, "bonus_hunter_share": 0.2, "dc_vpn_bot_share": 0.1, "chargeback_share": 0.03, "affiliate_fraud_share": 0, "no_visit_share": 0.05 } }}metrics पिछले 90 दिनों में एफ़िलिएट के अनुपात हैं, वही जो डैशबोर्ड रिपोर्ट दिखाती है:
पहली जमा तक पहुँचे पंजीकरणों का अनुपात (reg_to_ftd), पंजीकरण के 14 दिन बाद भी सक्रिय
खिलाड़ियों का अनुपात (active14_share), और डुप्लिकेट डिवाइस या व्यक्ति वाले, फ़्रॉड रिंग
से, बोनस हंटर से, डेटासेंटर, VPN या बॉट से आए, चार्जबैक या एफ़िलिएट फ़्रॉड के रूप में
लेबल किए गए, और बिना मिली विज़िट वाले कन्वर्ज़न के अनुपात। TRACIO केवल सिफ़ारिश करता है —
होल्ड स्वयं और भुगतान का कोई भी निर्णय आपके एफ़िलिएट प्लेटफ़ॉर्म में रहता है। पेलोड
वर्शन 2 पर वही बॉडी camelCase कुंजियों के साथ आती है: ब्लॉक affiliateThreshold है,
जिसमें affiliateId, previousRecommendation, qualityScore इत्यादि हैं।
transaction_state_changed इवेंटBusiness और उससे ऊपर। तब भेजा जाता है जब POST /v2/transactions पर आपके भेजे गए लेनदेन
का परिणाम बदलता है: नियमों ने REVIEW या DECLINE तय किया (APPROVE कुछ नहीं भेजता),
किसी विश्लेषक ने डैशबोर्ड में लेनदेन को समीक्षा के लिए भेजा, या कोई लेबल सेट, बदला या
हटाया गया। इसका उपयोग दावे की समीक्षा के दौरान बोनस रोकने और परिणाम तय होने पर उसे
रोके रखने या जारी करने के लिए करें, बिना पोलिंग के। * में यह इवेंट शामिल है; यह केवल
पेलोड वर्शन 2 या 3 वाले webhooks को डिलीवर होता है। किसी एनवायरनमेंट से बंधा webhook
इसे केवल उसी एनवायरनमेंट के लिए प्राप्त करता है।
{ "version": 3, "event": "transaction_state_changed", "event_id": "txn-state:7d1c2b3a-0000-4000-8000-000000000042:118", "timestamp": "2026-10-15T10:04:00Z", "transaction": { "id": "7d1c2b3a-0000-4000-8000-000000000042", "transaction_id": "bonus-claim-1842", "state": "REVIEW", // always the decision of the rules "fraud_score": 50, "effective_state": "DECLINE", // the outcome to act on "recommendations": ["withhold_bonus"], "label": "fraud_detection_bonus_abuse", "label_source": "api", // api | dashboard "label_at": "2026-10-15T10:04:00Z", "previous_effective_state": "REVIEW" // …applied_rules and evidence, as in the transaction response }}| कुंजी | अर्थ |
|---|---|
effective_state | जिस परिणाम पर कार्रवाई करनी है: विश्लेषक की या लेबल की स्थिति, यदि सेट हो, अन्यथा state |
recommendations | बोनस के साथ क्या करना है, effective_state से निकाला गया |
label, label_source, label_at | वर्तमान लेबल, वह कहाँ सेट हुआ (api या dashboard) और कब; लेनदेन पर कोई लेबल न हो तो तीनों null होते हैं |
previous_effective_state | इस बदलाव से पहले का effective_state; स्वचालित निर्णय पर भेजे गए इवेंट के लिए null |
ब्लॉक लेनदेन को वैसा दिखाता है जैसा वह इवेंट भेजते समय है: यदि दो बदलाव जल्दी-जल्दी
होते हैं, तो दोनों इवेंट नवीनतम स्थिति लाते हैं, हर एक अपने previous_effective_state और
timestamp के साथ। क्रम की गारंटी नहीं है — timestamp से क्रमबद्ध करें, और वर्तमान
स्थिति के लिए लेनदेन को API से पढ़ें। इवेंट id: स्वचालित निर्णय के लिए
txn-state:<id>:created और हर बाद के बदलाव के लिए txn-state:<id>:<n>। इवेंट यह नहीं
बताता कि लेबल किसने सेट किया — केवल यह कि वह कहाँ से आया।
reputation_changed इवेंटBusiness और उससे ऊपर। तब भेजा जाता है जब आपके संगठन द्वारा देखे गए किसी व्यक्ति की
क्रॉस-कस्टमर प्रतिष्ठा दूसरे बैंड में जाती है (उदाहरण के लिए clean → suspicious)।
इसके लिए पर्सन लेयर आवश्यक है, जो फ़िलहाल अवलोकन मोड में चलती है — इसलिए इवेंट केवल उन्हीं
वर्कस्पेस में ट्रिगर होता है जहाँ क्रॉस-डिवाइस पहचान समाधान सक्षम है। यह व्यक्ति-स्तर का
इवेंट है: इसमें न request_id होता है और न विज़िट ब्लॉक।
{ "version": 3, "event": "reputation_changed", "event_id": "person-reputation:pn_4f2a…:clean->suspicious:2026-07-30T12:10:00.123456Z", "timestamp": "2026-07-30T12:10:00Z", "reputation_change": { "person_id": "pn_4f2a…", "score": 46, "band": "suspicious", // clean | suspicious | fake "previous_band": "clean", "categories": ["chargeback"], "visitor_ids": ["X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"], // the person's visitors in YOUR data "linked_accounts_count": 4 // omitted when zero }}ब्लॉक की कुंजी reputation_change है, reputation नहीं: दूसरी कुंजी किसी विशिष्ट
विज़िट के संदर्भ में प्रतिष्ठा के लिए ली जा चुकी है, और दोनों अलग चीज़ें हैं। स्कीमा 1
ऐतिहासिक फ़्लैट रूप भेजता है — वही फ़ील्ड शीर्ष स्तर पर।
हर डिलीवरी में एक X-Tracio-Signature हेडर शामिल होता है:
X-Tracio-Signature: t=1710432000,v1=5257a869e7ecebed…t वह Unix टाइमस्टैम्प (सेकंड में) है जब अनुरोध पर हस्ताक्षर किए गए।v1 आपके webhook सीक्रेट से की-किया गया "<t>.<rawRequestBody>" का hex-एन्कोडेड
HMAC-SHA256 है।टाइमस्टैम्प हस्ताक्षरित सामग्री का हिस्सा है, जो रीप्ले सुरक्षा देता है।
दो बातें सही होनी चाहिए, वरना प्रोडक्शन में सत्यापन विफल हो जाएगा:
v1= मान के मिलान को स्वीकार करें। सीक्रेट रोटेशन के दौरान हेडर दो
सिग्नेचर लाता है, और जो पार्सर उनमें से केवल एक रखता है, वह पूरे रोटेशन विंडो में वैध
डिलीवरी अस्वीकार करता रहेगा।// Express.js exampleimport express from "express"import crypto from "crypto"
const app = express()
// Capture the raw body so the signature can be verified byte-for-byte.app.use( express.json({ verify: (req, _res, buf) => { ;(req as any).rawBody = buf }, }),)
function verifySignature(rawBody: Buffer, header: string, secret: string): boolean { if (!header) return false
const parts = header.split(",").map((p) => p.trim()) const ts = parts.find((p) => p.startsWith("t="))?.slice(2) if (!ts) return false
// Replay protection: reject timestamps more than five minutes old. if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
// Sign the raw bytes: the "<t>." prefix plus the raw request body. const signed = Buffer.concat([Buffer.from(`${ts}.`, "utf8"), rawBody]) const expected = crypto.createHmac("sha256", secret).update(signed).digest("hex") const exp = Buffer.from(expected, "hex")
// During a rotation window the header carries several v1= — any may match. return parts.some((p) => { if (!p.startsWith("v1=")) return false const got = Buffer.from(p.slice(3), "hex") // Compare lengths BEFORE timingSafeEqual: it throws on differing lengths, // and one junk header would turn the handler into a 500. return got.length === exp.length && crypto.timingSafeEqual(got, exp) })}
app.post("/webhook/tracio", (req, res) => { const header = req.headers["x-tracio-signature"] as string if (!verifySignature((req as any).rawBody, header, WEBHOOK_SECRET)) { return res.status(401).json({ error: "Invalid signature" }) }
const event = req.body console.log(`Visitor: ${event.visitor_id}`) console.log(`Bot: ${event.bot?.result}`) // "human" | "bot" | "uncertain"
res.status(200).send("OK")})स्कीमा 2 और 3 की डिलीवरी अतिरिक्त रूप से X-Tracio-Signature-Ed25519
(t=<unix>,kid=<id>,v1=<base64>) भी लाती हैं। HMAC सीक्रेट दोनों पक्ष जानते हैं,
इसलिए HMAC यह तो साबित करता है कि भेजने वाला सीक्रेट जानता है, पर यह नहीं कि अनुरोध
TRACIO से आया है; असममित सिग्नेचर यही साबित करता है। सार्वजनिक कुंजियाँ
https://api.tracio.ai/.well-known/webhook-keys पर kid के अनुसार प्रकाशित हैं:
public_key फ़ील्ड पढ़ें। पुराना उपनाम publicKey वही मान लाता है और 2027-04-12
को हटा दिया जाएगा।
डैशबोर्ड से भेजी गई टेस्ट डिलीवरी केवल HMAC से हस्ताक्षरित होती हैं — प्लेटफ़ॉर्म की
निजी कुंजी डिलीवरी नोड्स पर रहती है और जान-बूझकर डैशबोर्ड को उपलब्ध नहीं कराई जाती।
जो सत्यापनकर्ता Ed25519 को अनिवार्य करता है, उसे टेस्ट डिलीवरी को पास होने देना
चाहिए (उनके event_id पर :test सफ़िक्स होता है), वरना प्रोडक्शन स्वस्थ रहते हुए भी
डैशबोर्ड से टेस्टिंग विफल होगी। यही सावधानी फ़ॉर्मेट जांच पर भी लागू होती है: टेस्ट
डिलीवरी में request_id test_<hex> रूप में आता है और visitor_id के रूप में अक्षरशः
test_visitor — इसलिए जो हैंडलर इन्हें प्रोडक्शन के रूपों के विरुद्ध जांचता है, वह ऐसी
डिलीवरी अस्वीकार कर देगा जो बाकी सब लिहाज़ से सही बनी हुई है।
रोटेशन के बाद दोनों सीक्रेट 24 घंटे तक वैध रहते हैं और हेडर दोनों सिग्नेचर लाता है, जिससे आप डिलीवरी खोए बिना अपना कॉन्फ़िगरेशन अपडेट कर सकते हैं। Revoke now क्रिया इस विंडो को जल्दी बंद कर देती है। अपनी ओर का सीक्रेट 24 घंटे के भीतर अपडेट करें: विंडो बंद होते ही पुराना सीक्रेट मेल खाना बंद कर देता है, और यदि आपका एंडपॉइंट अमान्य सिग्नेचर पर 4xx देता है, तो लगातार पाँच ऐसे उत्तर webhook को निष्क्रिय कर देते हैं।
| हेडर | विवरण |
|---|---|
Content-Type | application/json |
X-Tracio-Signature | t=<unix>,v1=<hmac_sha256_hex> — रोटेशन विंडो के दौरान दो v1= |
X-Tracio-Signature-Ed25519 | प्लेटफ़ॉर्म सिग्नेचर, t=<unix>,kid=<id>,v1=<base64> (केवल v2 और v3) |
X-Tracio-Event-Id | डिलीवरी पहचानकर्ता — आइडेम्पोटेंसी कुंजी |
X-Tracio-Request-Id | विज़िट पहचानकर्ता, request_id से मेल खाता है (केवल v2 और v3)। केवल विज़िट इवेंट के लिए भेजा जाता है — identification, account_takeover और account_assessment |
X-Tracio-Event-Type | इवेंट का प्रकार (केवल v2 और v3) |
X-Tracio-Delivery-Attempt | प्रयास संख्या, 1 से शुरू (केवल v2 और v3) |
X-Tracio-Payload-Version | 3 या 2 (केवल v2 और v3) |
X-Tracio-Webhook-Id | उस webhook का पहचानकर्ता जिसने यह डिलीवरी बनाई |
डिलीवरी दोबारा भेजी जा सकती हैं, और रिट्राई वही X-Tracio-Event-Id लाता है। इसी पर
डीडुप्लिकेट करें:
app.post("/webhook/tracio", async (req, res) => { const eventId = req.headers["x-tracio-event-id"] as string
const existing = await db.webhooks.findOne({ eventId }) if (existing) return res.status(200).send("Already processed")
await db.webhooks.insert({ eventId, processedAt: new Date() }) await processWebhookEvent(req.body)
res.status(200).send("OK")})ध्यान दें कि event_id प्रति इवेंट अद्वितीय है, प्रति webhook नहीं: यदि वर्कस्पेस के
कई webhooks एक ही इवेंट की सदस्यता लेते हैं, तो हर एक को उसी पहचानकर्ता के साथ डिलीवरी
मिलेगी। identification के लिए यह <request_id>:<phase> के रूप में बनता है, इसीलिए एक
ही विज़िट के तीन फ़ेज़ आपस में मिलकर एक नहीं हो जाते, बल्कि स्वतंत्र रूप से डीडुप्लिकेट
होते हैं। बिना विज़िट वाले इवेंट के अपने रूप हैं — ऊपर हर इवेंट देखें।
2xx के साथ उत्तर दें — यही एकमात्र संकेत है कि डिलीवरी स्वीकार की गई।
| उत्तर | क्या होता है |
|---|---|
2xx | डिलीवरी पूरी |
429 Too Many Requests | विफलता नहीं गिनी जाती और कोई प्रयास खर्च नहीं होता; लंबा Retry-After माना जाता है |
408, 425, 5xx, कनेक्शन टूटना | बढ़ते हुए अंतराल के साथ दोबारा प्रयास |
410 Gone | एंडपॉइंट हटा हुआ माना जाता है — webhook तुरंत निष्क्रिय |
अन्य 4xx | दोबारा प्रयास होता है, पर लगातार पाँच webhook निष्क्रिय कर देते हैं — 400/401/404 रिट्राई से ठीक नहीं होते |
रिट्राई शेड्यूल: 5s → 30s → 2min → 10min → 30min → 2h → 6h (8 प्रयास)। पहले कुछ रिट्राई एक मिनट के भीतर हो जाते हैं, इसलिए आपकी सेवा के छोटे रीस्टार्ट से कोई सूचना नहीं खोती। हर अंतराल आधे और पूरे मान के बीच रैंडमाइज़ किया जाता है, ताकि किसी आउटेज के बाद सारे रिट्राई एक साथ न चलें।
ऑटो-डिसेबल के लिए थ्रेशोल्ड (लगातार 20 विफलताएँ, या 5 कॉन्फ़िगरेशन त्रुटियाँ) और कम से कम 15 मिनट लगातार विफलता — दोनों आवश्यक हैं; छोटा रीस्टार्ट इंटीग्रेशन को खत्म नहीं कर सकता, भले ही बहुत-सी डिलीवरी कतार में लग गई हों। 15 मिनट से लंबा अंतराल गिनती दोबारा शुरू कर देता है। डैशबोर्ड कारण दिखाता है, उत्तर कोड और त्रुटि पाठ के साथ, और एक Re-enable बटन देता है जो काउंटर रीसेट कर देता है।
| प्लान | प्रति वर्कस्पेस webhooks |
|---|---|
| Free | उपलब्ध नहीं |
| Pro | 5 |
| Business | 20 |
| Enterprise | 100 |
एंडपॉइंट सार्वजनिक IP वाले https होने चाहिए — निजी और लूपबैक पते अस्वीकार किए जाते
हैं, रीडायरेक्ट पर भी — और दो से अधिक रीडायरेक्ट गहरे नहीं।
केवल 307 और 308 रीडायरेक्ट का पालन किया जाता है। 301, 302 और 303 क्लाइंट
को GET पर स्विच करने और बॉडी हटाने का निर्देश देते हैं, इसलिए डिलीवरी उनका पालन नहीं
करती और वह प्रयास विफल गिना जाता है। यदि आपका लोड बैलेंसर URL को नॉर्मलाइज़ करता है
(www या अंत में स्लैश जोड़कर), तो webhook को सीधे अंतिम URL पर इंगित करें।
Webhooks का प्रबंधन डैशबोर्ड में होता है। डैशबोर्ड एक वर्कस्पेस-स्कोप्ड प्रबंधन API
चलाता है, जो एप्लिकेशन होस्ट पर उपलब्ध है (उदाहरण के लिए
https://app.tracio.ai/api/v1), और नीचे दिए गए एंडपॉइंट ठीक वही हैं जिन्हें वह कॉल
करता है। हर webhook एंडपॉइंट /workspaces/{wsId} के अंतर्गत है।
यह सर्वर-से-सर्वर सतह नहीं है। प्रबंधन API केवल आपके डैशबोर्ड सत्र का JWT स्वीकार करता है, जिसे आपकी वर्कस्पेस भूमिका (RBAC) के विरुद्ध जांचा जाता है;
tracio_sk_…सीक्रेट कुंजी यहाँ अस्वीकार कर दी जाती है। चूँकि वह सत्र ब्राउज़र में रहता है और उसी के साथ समाप्त हो जाता है, नीचे दिए कॉल को स्वचालित करने लायक इंटीग्रेशन नहीं, बल्कि इस बात का विवरण मानें कि डैशबोर्ड क्या करता है। अपने बैकएंड से प्रोग्राम द्वारा पहुँच के लिए केवल-पढ़ने वाला Data API उपयोग करें।
curl -X POST https://app.tracio.ai/api/v1/workspaces/{wsId}/webhooks \ -H "Authorization: Bearer <session-jwt>" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhook/tracio", "events": [] }'साइनिंग सीक्रेट TRACIO द्वारा जनरेट किया जाता है और बनाने पर (तथा रोटेट करने पर)
signingSecret के अंतर्गत एक ही बार लौटाया जाता है। इसे सुरक्षित रखें — सिग्नेचर
सत्यापित करने के लिए आप यही कुंजी उपयोग करते हैं।
{ "ok": true, "data": { "id": "b3d4f8a1-2c67-4e9b-8f05-7a1d3c9e2b48", "workspaceEnvironmentId": "b201f2ba-…", "url": "https://your-server.com/webhook/tracio", "events": [], "signingSecret": "f3a9…<hex>", "status": "active", "successRate": 100, "createdAt": "2026-07-30T12:00:00Z" }}बाद के रीड में signingSecret मास्क कर दिया जाता है (null) — यह केवल बनाने और सीक्रेट
रोटेट करने पर ही दिखाया जाता है।
| मेथड | पथ | विवरण |
|---|---|---|
GET | /workspaces/{wsId}/webhooks | webhooks की सूची |
PATCH | /workspaces/{wsId}/webhooks/{webhookId} | url / events / status अपडेट करें |
DELETE | /workspaces/{wsId}/webhooks/{webhookId} | webhook हटाएं |
POST | /workspaces/{wsId}/webhooks/{webhookId}/test | हस्ताक्षरित टेस्ट डिलीवरी भेजें |
POST | /workspaces/{wsId}/webhooks/{webhookId}/secret/rotate | साइनिंग सीक्रेट रोटेट करें |
GET | /workspaces/{wsId}/webhooks/{webhookId}/deliveries | हाल के डिलीवरी प्रयासों की सूची |
टाइमआउट से बचने के लिए जितनी जल्दी हो सके 2xx लौटाएं और पेलोड को अतुल्यकालिक रूप से
प्रोसेस करें:
app.post("/webhook/tracio", async (req, res) => { res.status(200).send("OK") processWebhookEvent(req.body).catch(console.error)})
async function processWebhookEvent(event: WebhookPayload) { await db.events.insert(event)
if (event.decision?.risk_score > 50) { await alertFraudTeam(event) }
if (event.bot?.result === "bot") { await blockVisitor(event.visitor_id) }}किसी webhook पर Test क्रिया (या POST .../webhooks/{webhookId}/test) का उपयोग करके
अपने एंडपॉइंट पर हस्ताक्षरित नमूना पेलोड भेजें और पुष्टि करें कि वह पहुँच योग्य है और
सिग्नेचर सही ढंग से सत्यापित कर रहा है।
लोकल डेवलपमेंट के लिए, अपने सर्वर को ngrok जैसी टनल से एक्सपोज़ करें:
ngrok http 3000# Use the generated URL as your webhook endpoint