ה-webhooks מוסרים אירועי זיהוי לשרת שלכם בזמן אמת. בכל פעם שמבקר מזוהה, TRACIO שולחת
בקשת POST ב-HTTP לכתובת ה-webhook שהגדרתם. גוף הבקשה הוא מטען (payload) האירוע.
הם גם הערוץ היחיד שמוסר הכרעות מאוחרות — אלה שבהן התנהגות המבקר הוכיחה שמדובר באוטומציה אחרי שהעמוד כבר נטען.
הגדירו אותם בלוח הבקרה תחת 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 | ההמלצה עבור שותף (affiliate) החמירה (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 — סדר גודל של נפח שונה משאר האירועים.
סוגי סיכון ההתחברות והשותפים הגיוניים רק לאינטגרציה שקוראת ל-APIs האלה. תו כללי
שהגדרתם לפני שהאירועים האלה היו קיימים אינו הסכמה לקבל אותם.
סוג לא מוכר נדחה עם 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", משום ששניהם מורמים
מתוך ה-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 (ברמת ה-workspace), 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. Webhooks קיימים בגרסה 1 או 2 שומרים על הגרסה שבחרו; שתי הסכמות הישנות מוקפאות ויוסרו ב-2027-04-12. כדי לעבור, בחרו בגרסה 3 בכרטיס ה-webhook בלוח הבקרה.
| גרסה | סטטוס |
|---|---|
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 | מזהה המסירה — מפתח ה-idempotency; ייחודי לכל אירוע ומשותף לכל ה-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 | כתובת העמוד שבו נלכד האירוע |
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 — ארכיטקטורת המעבד ורוחב הסיביות, דגם המכשיר (ב-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 ומעלה). ברגע שמצב האדם מופעל עבור ה-workspace שלכם,
הגוף נושא גם 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 הוא המחמיר ביותר מבין כל ארבעת התרחישים.
| המלצה | תשלום | הרשמה | התחברות | שותפים (affiliate) |
|---|---|---|---|---|
allow | בצעו אותו | צרו אותו | אפשרו כניסה | זכו את ההמרה |
challenge | 3-D Secure / אישור | קפצ׳ה, אישור באימייל או בטלפון | 2FA מוגברת, אימות מחדש | סמנו כמפוקפקת עד שתתגלה פעילות |
review | בצעו, אך העבירו לתור לבדיקה | צרו עם הגבלות | אפשרו כניסה והפעילו התראה | עצרו את התשלום עד לבדיקה |
deny | אל תבצעו את העסקה | סרבו ליצור את החשבון | אל תאפשרו כניסה | אל תזכו את ההמרה |
basis מפרט את הצירים שקבעו את ההמלצה — ציר שהצעתו הייתה מתונה מזו שאומצה אינו
נכנס לרשימה. קראו אותו לפי קוד במקום להניח מערך קבוע:
ציר (basis) | משמעות |
|---|---|
list | המבקר תאם לאחת מרשימות ה-workspace שלכם במצב enforce. רשימת allow גוברת על כל ציר: כל תרחיש הופך ל-allow ו-basis הוא בדיוק ["list"] |
rule | הביקור תאם לאחד מכללי ה-workspace שלכם במצב 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, ולהפך.
ה-guidance הוא מצטבר: תרחישים חדשים מגיעים כמפתחות חדשים בלי לשבור את החוזה. השלב
המאוחר מנצח, למעט המלצה חלקית: מסירה שחושבה על מערך קלטים לא שלם מסומנת
"partial": true, והמלצה חלקית אינה דורסת המלצה מלאה שהתקבלה קודם לכן עבור אותו
request_id. במסירה רגילה השדה partial נעדר לחלוטין.
הספים המדויקים אינם מתועדים במכוון. המלצה שניתן להנדס לאחור לכדי ציון חדלה מלהיות
הגנה. יש בדיוק חריג מכוון אחד: account_assessment.verdict.threshold, גבול ההחלטה של
הכרעת ההשתלטות על החשבון שמתואר להלן.
account_takeoverBusiness ו-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", // ההתראה מורמת מתוך 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_assessmentBusiness ו-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 | נבחן; ההתנהגות חורגת | Step-up — ההתראה 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
הבלוק נעדר ואינו ממולא באפסים — score של 0 לצד threshold של 0 היה נקרא כ„ההתנהגות
תאמה בדיוק“.
| שדה | משמעות |
|---|---|
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. אין לה שדות זיהוי — זהו
אירוע ברמת ה-workspace:
{ "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 }}Webhooks בגרסת מטען 1 מקבלים את סכמת ההתראה השטוחה ההיסטורית — היא אינה משתנה.
login_risk, trusted_device_enrolled ו-trusted_device_revokedשלושת האירועים האלה נובעים מהקריאות שלכם ל-API של סיכון ההתחברות
(POST /v2/logins/assess) ולמכשירים המהימנים שלו. הם נמסרים רק במנוי מפורש לפי
שם — * אינו כולל אותם — ורק ל-webhooks בגרסת מטען 2 או 3; לגרסה 1 אין עבורם גוף.
login_risk נשלח כאשר הערכה מחזירה challenge או review — לעולם לא ב-allow,
deny או not_assessed. בלוק ה-login_risk שלו הוא ההערכה השמורה, בדיוק כפי
שהתשובה הסינכרונית נתנה אותה. מזהה האירוע: 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.
מזהה האירוע: 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נמסר רק במנוי מפורש לפי שם — * אינו כולל אותו — ורק ל-webhooks בגרסת מטען 2
או 3. כל 15 דקות TRACIO מחשבת מחדש את ציון האיכות של כל שותף עם מספיק נתונים ושולחת
את האירוע כאשר ההמלצה שלו מחמירה: none → review, none → hold או review → hold.
שיפור אינו שולח דבר ומאפשר לדווח שוב על ההחמרה הבאה. האירוע שייך ל-workspace: הוא
נשלח ל-webhooks הרשומים של כל סביבה ואינו נושא את הכותרת X-Tracio-Request-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 או בוט, המסומנות כ-chargeback או כהונאת שותפים,
וללא ביקור שנמצא. TRACIO רק ממליצה — העיכוב עצמו וכל החלטת תשלום נשארים בפלטפורמת
השותפים שלכם. בגרסת מטען 2 לאותו גוף יש מפתחות camelCase: הבלוק הוא affiliateThreshold,
עם affiliateId, previousRecommendation, qualityScore וכן הלאה.
transaction_state_changedBusiness ומעלה. נשלח כאשר התוצאה של עסקה ששלחתם ל-POST /v2/transactions משתנה:
הכללים החליטו REVIEW או DECLINE (APPROVE אינו שולח דבר), אנליסט שלח את העסקה
לבדיקה בלוח הבקרה, או שתווית הוגדרה, הוחלפה או הוסרה. השתמשו בו כדי לעכב בונוס בזמן
שתביעה בבדיקה ולמנוע או לשחרר אותו ברגע שהתוצאה הוכרעה, בלי תשאול חוזר. * כולל את
האירוע הזה; הוא נמסר רק ל-webhooks בגרסת מטען 2 או 3. 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 כדי לקבל את מצבה הנוכחי. מזהה האירוע: txn-state:<id>:created עבור ההחלטה
האוטומטית ו-txn-state:<id>:<n> עבור כל שינוי מאוחר יותר. האירוע אינו אומר מי הגדיר
תווית — רק מאיפה היא הגיעה.
reputation_changedBusiness ומעלה. נשלח כאשר המוניטין חוצה-הלקוחות של אדם שהארגון שלכם ראה עובר לרצועה
אחרת (למשל clean ← suspicious). הוא דורש את שכבת האדם, שפועלת כרגע במצב תצפית —
ולכן האירוע נורה רק עבור workspaces שבהם הופעל זיהוי זהות חוצה-מכשירים. זהו אירוע
ברמת האדם: אין לו 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 הוא חותמת הזמן של יוניקס (בשניות) שבה הבקשה נחתמה.v1 הוא HMAC-SHA256 של "<t>.<rawRequestBody>" בקידוד הקסדצימלי, עם מפתח שהוא
הסוד של ה-webhook שלכם.חותמת הזמן היא חלק מהתוכן הנחתם, וזה מה שמעניק הגנה מפני replay.
שני דברים שחייבים לעשות נכון, אחרת האימות ייכשל בייצור:
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 באופן נוקשה חייב
להעביר מסירות בדיקה (הן נושאות סיומת :test ב-event_id), אחרת בדיקה מלוח הבקרה
נכשלת בזמן שהייצור תקין. אותה זהירות חלה גם על בדיקות פורמט: מסירת בדיקה נושאת
request_id בצורה test_<hex> ואת המילולי test_visitor בתור visitor_id, כך שמטפל
שמאמת אותם מול הצורות של הייצור ידחה מסירה שתקינה מכל בחינה אחרת.
לאחר החלפה שני הסודות נשארים תקפים 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 | מזהה המסירה — מפתח ה-idempotency |
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 ב-workspace
רשומים לאותו אירוע, כל אחד מהם יקבל מסירה עם אותו מזהה. עבור identification הוא נבנה
כ-<request_id>:<phase>, ולכן שלושת שלבי הביקור עוברים דה-דופליקציה בנפרד זה מזה במקום
להתמזג לאחד. לאירועים בלי ביקור יש צורות משלהם — ראו כל אירוע למעלה.
השיבו בקוד 2xx — זה הסימן היחיד לכך שמסירה התקבלה.
| תגובה | מה קורה |
|---|---|
2xx | המסירה הושלמה |
429 Too Many Requests | לא נספרת ככישלון ואינה מבזבזת ניסיון; Retry-After ארוך יותר יכובד |
408, 425, 5xx, ניתוק חיבור | נשלחת שוב עם השהיה גדלה |
410 Gone | נקודת הקצה נחשבת כמוסרת — ה-webhook מושבת מיידית |
4xx אחרים | נשלחת שוב, אך חמש ברצף משביתות את ה-webhook — 400/401/404 לא נרפאים בניסיון חוזר |
לוח הניסיונות החוזרים: 5 שנ׳ ← 30 שנ׳ ← 2 דק׳ ← 10 דק׳ ← 30 דק׳ ← 2 שע׳ ← 6 שע׳ (8 ניסיונות). הניסיונות הראשונים נכנסים בתוך דקה, כך שהפעלה מחדש קצרה של השירות שלכם לא עולה לכם בהתראה. כל השהיה מוגרלת בין מחצית הערך לערך המלא, כדי שניסיונות חוזרים לא יירו במטח אחד אחרי תקלה.
השבתה אוטומטית מחייבת גם סף (20 כשלים רצופים, או 5 שגיאות תצורה) וגם לפחות 15 דקות רצופות של כשלים — הפעלה מחדש קצרה אינה יכולה להרוג את האינטגרציה גם אם מסירות רבות המתינו בתור. פער ארוך מ-15 דקות מאפס את הספירה. לוח הבקרה מציג את הסיבה, לצד קוד התגובה וטקסט השגיאה, וכפתור Re-enable שמאפס את המונים.
| תוכנית | Webhooks לכל workspace |
|---|---|
| Free | לא זמין |
| Pro | 5 |
| Business | 20 |
| Enterprise | 100 |
נקודות הקצה חייבות להיות https עם כתובת IP ציבורית — כתובות פרטיות ו-loopback
נדחות, כולל בהפניה — ולא יותר משתי הפניות לעומק.
נעקבות רק הפניות 307 ו-308. 301, 302 ו-303 מורות ללקוח לעבור ל-GET
ולזרוק את הגוף, ולכן מסירה אינה עוקבת אחריהן והניסיון נספר ככישלון. אם מאזן העומסים
שלכם מנרמל את הכתובת (מוסיף www או לוכסן בסוף), הפנו את ה-webhook ישירות לכתובת
הסופית.
ה-webhooks מנוהלים בלוח הבקרה. לוח הבקרה מפעיל management API בהיקף workspace,
המוגש על מארח האפליקציה (למשל https://app.tracio.ai/api/v1), ונקודות הקצה שלהלן הן
בדיוק אלה שהוא קורא להן. כל נקודות הקצה של ה-webhooks נמצאות תחת /workspaces/{wsId}.
זהו אינו ממשק שרת-לשרת. ה-management API מקבל רק את ה-JWT של סשן לוח הבקרה שלכם, הנבדק מול התפקיד שלכם ב-workspace (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 מהר ככל האפשר ועבדו את המטען באופן אסינכרוני כדי להימנע מ-timeouts:
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) }}השתמשו בפעולה Test על webhook (או ב-POST .../webhooks/{webhookId}/test) כדי
לשלוח מטען לדוגמה חתום לנקודת הקצה שלכם ולוודא שהיא נגישה ומאמתת חתימות כראוי.
לפיתוח מקומי, חשפו את השרת שלכם באמצעות מנהרה כמו ngrok:
ngrok http 3000# Use the generated URL as your webhook endpoint