Webhook'lar kimlik tespiti olaylarını sunucunuza gerçek zamanlı olarak iletir. Bir
ziyaretçi her tanımlandığında TRACIO, yapılandırdığınız webhook URL'sine bir HTTP
POST isteği gönderir. İsteğin gövdesi olayın payload'ının ta kendisidir.
Ayrıca geç kararları ileten tek kanaldır: ziyaretçinin davranışının, sayfa yüklendikten sonra onun otomatikleştirilmiş olduğunu kanıtladığı kararları.
Bunları panelde Settings → Webhooks altında ayarlayın. Webhook'lar Pro veya daha üst bir plan gerektirir.
| Olay | Ne zaman | Plan |
|---|---|---|
identification | Her ziyarette — primary, late ve correction fazları | Tümü |
account_takeover | Bir hesap altındaki davranış artık sahibinin profiline uymuyor | Business+ |
attack_detected | Sitenizde bot sayısında ani artış | Business+ |
reputation_changed | Bir cihazın arkasındaki kişinin itibarı değişti | Business+ |
Olay adları noktayla değil, her zaman alt çizgiyle yazılır — visitor.created veya
session.created diye bir şey yoktur. reputation_changed kişi katmanını gerektirir;
bu nedenle yalnızca cihazlar arası kimlik çözümlemesinin etkin olduğu workspace'lerde
tetiklenir.
Bir webhook belirli türlere abone olur; ayrı bir değer olan * ise "sonradan
eklenenler dahil her tür" anlamına gelir. Abonelik oluşturulurken veya düzenlenirken
bilinmeyen bir tür 400 ile reddedilir; böylece bir yazım hatası sizi sessizce hiç
tetiklenmeyen bir webhook'la baş başa bırakmaz.
identification olayının fazlarıTek bir ziyaret, aynı requestId'yi paylaşan en fazla üç teslimat üretir:
primary — sayfa yüklenirken verilen ilk karar.late — yavaş kontroller tamamlandığında, yaklaşık dokuz saniye sonraki zenginleştirme.correction — davranışa dayalı düzeltme (işaretçi, klavye, kaydırma).Bunları requestId ile ilişkilendirin, phase ile birbirinden ayırın. Sonraki faz
önceliklidir: primary human dediyse ve correction bot diyorsa, doğru cevap
ikincisidir.
Geliş sırasına güvenmeyin. Her faz bağımsız olarak ve kendi yeniden deneme
takvimiyle teslim edilir; primary yeniden denemeye düşmüşken late ilk denemede
başarılı olduysa, bunları ters sırada alırsınız. Önceliği alınma zamanından değil,
phase alanından belirleyin.
Bir identification olayının fazları yalnızca bu üçüdür. Size ulaşan bir değer daha
vardır: account_takeover, phase: "beacon" taşır; çünkü hesap ele geçirme uyarısı
her zaman davranışsal bir beacon'dan yükseltilir.
Bunun yarattığı uyumsuzluğa dikkat edin, çünkü idempotency'yi etkiler. Üretimdeki bir
identification teslimatının eventId değeri tam olarak <requestId>:<phase>
biçimindedir, ancak iki teslimat bu formülü bozar. Bir account_takeover
<requestId>:ato olur — sonek, phase alanının değeri değil, birebir ato dizesidir.
Panelden gönderilen bir test teslimatı ise <requestId>:test olur; buna karşılık şema
2 gövdesindeki phase hâlâ primary yazar — şema 1 gövdesinde ise phase alanı hiç
bulunmaz, dolayısıyla bu sonekin göründüğü tek yer başlıktır. eventId değerini
doğrudan idempotency anahtarı olarak kullanın ve onu asla requestId ile phase'ten
yeniden birleştirmeyin. İşlediğiniz değerlerle eşleştirin; geri kalanını teslimatı
reddetmek yerine yok sayın.
attack_detected workspace düzeyinde bir olaydır: requestId'si, visitorId'si ve
browser, geo, bot ya da decision bloklarının hiçbiri yoktur — bu anahtarlar
basitçe bulunmaz. account_takeover belirli bir ziyaretten doğar ve planınıza ait tam
kimlik tespiti gövdesini artı bir accountAlert bloğunu taşır. Tüm olayları tek bir
handler'da ayrıştırıyorsanız, ziyaret alanlarına dokunmadan önce event alanını
kontrol edin.
| Sürüm | Kimin için | Nasıl değiştirilir |
|---|---|---|
1 | v2 var olmadan önce oluşturulan webhook'lar | Onlar için varsayılan olarak kalır |
2 | Yeni webhook'lar | Paneldeki webhook kartındaki anahtar |
v1 şeması dondurulmuştur — hiçbir alanı değişmez, dolayısıyla mevcut entegrasyonlar düzenleme gerekmeden çalışmaya devam eder. Yeni olan her şey v2'de yaşar; yeni webhook'ların ürettiği de budur.
{ "version": 2, "event": "identification", "eventId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31:primary", // "<requestId>:<phase>" — the idempotency key "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", // visit identifier, shared by all phases "phase": "primary", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "linkedId": "user-42", // your ?lid=, if you passed one "tag": "checkout", "timestamp": "2026-07-30T12:00:00Z", "url": "https://shop.example.com/checkout", "ip": "203.0.113.44", "userAgent": "Mozilla/5.0 …", "browser": { "name": "Chrome", "version": "138" }, "os": { "name": "macOS", "version": "15.5" }, "device": "desktop", "gpu": "Intel Iris Plus Graphics 655", // ekran kartı modeli, normalleştirilmiş; bilinmediğinde bulunmaz "geo": { "country": "DE", "city": "Berlin", "lat": 52.52, "lon": 13.405, "timezone": "Europe/Berlin" }, "network": { "vpn": false, "proxy": true, "tor": false, "datacenter": true, "connectionType": "DCH", "proxyDetected": true // HTTP trafiği ile ham ağ yolu farklı ağlardan çıkıyor }, "bot": { "result": "human", "score": 3.2 }, // human | bot | uncertain "identification": { "confidence": 0.97, "incognito": false }, "decision": { "action": "real", "riskScore": 12.2 } // real | fake | suspicious}Sıfır ve boş değerler çıkarılır. Sıfır değerli metin ve sayı alanları (örneğin bir
insan için bot.type) JSON'da bulunmaz — bunları şemalarınızda zorunlu yapmayın ve iç
içe blokları savunmacı biçimde okuyun.
bot.score ve decision.riskScore, virgülden sonra tek basamaklı, 0..100
ölçeğinde ondalıklı sayılardır — panelin aynı ziyaret için raporladığı sayıların
tıpatıp aynısı. (Dondurulmuş v1 şemasında farklı birimler kullanılır: sırasıyla 0..1
aralığında bir kesir ve 0..255.)
bot.type ya tanınan bir botun adıdır ya da bir aile etiketidir. Sözlük için
Bot türleri bölümüne bakın — dahili kontrol adları
hiçbir planda açığa çıkarılmaz.
| Alan | Tür | Açıklama |
|---|---|---|
version | number | Payload şema sürümü (2) |
event | string | Olay türü |
eventId | string | Teslimat tanımlayıcısı — idempotency anahtarı |
requestId | string | Ziyaret tanımlayıcısı (UUID); ziyaretin tüm fazlarınca paylaşılır |
phase | string | primary, late, correction; account_takeover beacon taşır |
visitorId | string | Kararlı ziyaretçi tanımlayıcısı |
linkedId | string | İstemcinin sağladığı bağlı tanımlayıcı |
tag | string | İstemcinin sağladığı özel etiket |
timestamp | string | Olay zamanı (RFC 3339) |
url | string | Olayın yakalandığı sayfanın URL'si |
ip | string | İstemcinin IP adresi |
userAgent | string | İstemcinin ham user-agent dizesi |
browser.name / .version | string | Tespit edilen tarayıcı |
os.name / .version | string | Tespit edilen işletim sistemi |
device | string | Cihaz sınıfı (ör. desktop, mobile) |
gpu | string | Tarayıcının bildirdiği (WebGL) ekran kartı modeli, okunabilir bir ada normalleştirilir (Intel Iris Xe Graphics, Apple M1 Pro); yazılım rasterleştiricilerde Software renderer; bilinmediğinde bulunmaz |
geo | object | IP coğrafi konumu: country, city, lat, lon, timezone |
network | object | vpn, proxy, tor, datacenter (boolean) ve connectionType |
network.proxyDetected | boolean | Ziyaretin HTTP trafiği ile ham ağ yolları farklı ağlardan çıkıyor — tarayıcının önünde bir proxy ya da VPN var. Aynı sağlayıcının iki adresi (operatör NAT'ı, aynı VPN'in ikinci çıkışı) sayılmaz |
bot.result | string | human, bot veya uncertain |
bot.type | string | Bot tespit edildiğinde bot adı veya aile etiketi |
bot.score | number | Bot skoru (0–100) |
identification.confidence | number | Kimlik tespiti güveni (0.0–1.0) |
identification.incognito | boolean | Özel/gizli tarama bağlamı |
decision.action | string | real, fake veya suspicious |
decision.riskScore | number | Toplam risk skoru (0–100) |
Pro ve üzeri — ziyaretçinin zaman içindeki davranışı:
{ "identification": { "matchType": "exact", // exact | fuzzy | new — how the visitor was recognized "matchConfidence": 0.93, "visits": 42, "incognitoVisits": 3 }, // Present when visitor counters are available at event time (usually primary). // A missing block means "no data", not "zeros". "velocity": { "events5m": 7, "uniqueIps": 2, "uniqueLocations": 1 }, "bot": { "antidetectScore": 0 }, // antidetect indicators, 0..100 "session": { "durationSeconds": 95 } // where the visit duration is already known}Business ve üzeri — kararın neden böyle çıktığı:
{ "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 }, "identification": { "driftScore": 0.31 }, // divergence from the account profile "deviceInfo": { "deviceId": "…", // the physical device across browsers on it "crossBrowser": true, "confidence": 0.88, "linkedBrowsers": 3 }, "network": { "isp": "Deutsche Telekom", "asn": 3320, // ham ağ yolunda gözlenen genel adres, yani proxy veya VPN'in arkasındaki // adres; böyle bir adres gözlenmediğinde bulunmaz "realIp": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" } }, // Bir Linux makinesinde ÖLÇÜLEN masaüstü ortamı ("Mint 22+", "Ubuntu", // "GNOME", "KDE"); bir User-Agent dağıtımı ifade edemez. // Belirlenemediğinde bulunmaz — Linux ziyaretlerinin çoğunda ve Linux // olmayan her ziyarette. "osEnvironment": "Mint 22+", // Ziyaretin kendisi hakkında ileri sürdüğü ile bağımsız denetimlerin // ölçtüğü. Yalnızca gerçekten bir sahtecilik saptandığında bulunur; boş // bir `real` alanı "denetim sessiz kaldı" demektir, asla "doğrulandı" // değil. `gpu` ekseninde `claimed.gpu`, ileri sürülen kartı üst düzey // `gpu` alanıyla aynı okunabilir model adıyla taşır. "spoofing": { "detected": true, "claimed": { "os": "Windows 10", "browser": "Chrome 139.0" }, "real": { "os": "macOS" }, "spoofedAxes": ["os", "screen"], // os | gpu | screen | network | browser "anonymousBrowser": { "detected": true, "names": ["Linken Sphere"] } }, // Cihaz bilgileri — ziyaretçinin tarayıcısının makine hakkında // bildirdikleri, bizim tarafımızda temizlenmiş olarak. screen: çözünürlük, // renk derinliği ve device pixel ratio. locale: tarayıcının kendi tercih // ettiği diller ve saat dilimi — IP adresinden türetilen geo.timezone'un // aksine; ikisi arasındaki uyuşmazlık, sahte konumun sık görülen bir // işaretidir. clientHints: User-Agent Client Hints — işlemci mimarisi ve // bit genişliği, cihaz modeli (Android: `model` alanındaki model kodu, // örn. "SM-A556B" ve Google Play cihaz listesinden gelen pazarlama adı // `deviceName` alanında, örn. "Samsung Galaxy A55 5G") ve tam platform // sürümü; bunları yalnızca Chromium tabanlı tarayıcılar bildirir. Ziyaret // böyle veri taşımadığında her blok bulunmaz, bu yüzden her birini isteğe // bağlı sayın. "screen": { "width": 2560, "height": 1600, "colorDepth": 30, "pixelRatio": 2 }, "locale": { "languages": ["en-US", "de"], "timezone": "Europe/Berlin" }, "clientHints": { "architecture": "arm", "bitness": "64", "platformVersion": "15.5.0" }, // Yalnızca ekran kartı kendisini sanal olarak tanıttığında bulunur; // hypervisor kapalı bir sözlüktür (vmware, virtualbox, parallels, qemu, // hyperv, bochs, intel-gvt, vgpu). Bloğun bulunmaması, böyle bir kanıt // olmadığı anlamına gelir. "environment": { "virtualMachine": true, "hypervisor": "vmware" }, "decision": { "suspectScore": 55 }, "guidance": { "version": 1, "overall": "review" } // see below}Neden kodu sözlüğü ve severity'nin ne anlama geldiği için
Bot Tespiti sayfasına bakın.
guidance, her entegrasyon noktası için hazır "ne yapmalı" önerileri taşır; böylece
ham skorlardan bir politika türetmek zorunda kalmazsınız:
{ "guidance": { "version": 1, "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 }}Her senaryo allow ile başlar ve merdivende yalnızca yukarı doğru hareket eder:
allow → challenge → review → deny. Bir senaryo içinde tetiklenen en katı eksen
kazanır; overall ise dört senaryonun tümü arasındaki en katı olandır.
| Öneri | Ödeme | Kayıt | Giriş | Affiliate |
|---|---|---|---|---|
allow | İşleyin | Oluşturun | İçeri alın | Dönüşümü hesaba yazın |
challenge | 3-D Secure / onay | Captcha, e-posta veya telefon onayı | Step-up 2FA, yeniden kimlik doğrulama | Aktivite görülene kadar şüpheli işaretleyin |
review | İşleyin, ancak incelemeye alın | Kısıtlamalarla oluşturun | İçeri alın, bir uyarı oluşturun | İnceleme yapılana kadar ödemeyi bekletin |
deny | İşlemi gerçekleştirmeyin | Hesabı oluşturmayı reddedin | İçeri almayın | Dönüşümü hesaba yazmayın |
version, kural setinin sürümüdür — mantık geliştikçe artırılır. Guidance eklemelidir:
yeni senaryolar sözleşmeyi bozmadan yeni anahtarlar olarak gelir. Sonraki faz kazanır,
kısmi öneri hariç: eksik bir girdi kümesi üzerinde hesaplanan bir teslimat
"partial": true olarak işaretlenir ve kısmi öneri, aynı requestId için daha önce
alınmış tam öneriyi geçersiz kılmaz. Olağan bir teslimatta partial alanı hiç
bulunmaz.
Kesin eşikler bilinçli olarak belgelenmez. Tersine mühendislikle bir skora dönüştürülebilen öneri, savunma olmaktan çıkar.
account_takeover olayıYalnızca Business ve Enterprise. Gövde, planınıza ait tam kimlik tespiti zarfı artı bir
accountAlert bloğudur ve ziyaret başına en fazla bir kez teslim edilir:
{ "version": 2, "event": "account_takeover", "eventId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31:ato", "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "phase": "beacon", // uyarı bir beacon'dan yükseltilir; yalnızca eventId "ato" der "accountAlert": { "type": "behavior-drift", "accountId": "user-42", // your linkedId for the account "driftScore": 0.83 } // …the remaining identification fields}v1'de bu blok type, linkedId ve drift taşır; v2'de iki alan yeniden
adlandırılmıştır — linkedId → accountId ve drift → driftScore. payloadVersion
değerini değiştirdiğinizde handler'ınızı güncelleyin; aksi hâlde hesap ele geçirme
mantığınız veriyi sessizce görmeyi bırakır.
attack_detected olayı{ "version": 2, "event": "attack_detected", "eventId": "c0a8e1f2-…", "timestamp": "2026-07-30T12:00:00Z", "attack": { "kind": "bot_spike", "severity": "critical", // info | warning | critical "windowMinutes": 15, "recentBots": 4210, "recentTotal": 5100, "expected": 180.5 // the baseline expected over a window this size }}Her teslimat bir X-Tracio-Signature başlığı içerir:
X-Tracio-Signature: t=1710432000,v1=5257a869e7ecebed…t, isteğin imzalandığı Unix zaman damgasıdır (saniye).v1, webhook gizli anahtarınızla anahtarlanmış "<t>.<rawRequestBody>" değerinin
onaltılık kodlanmış HMAC-SHA256'sıdır.Zaman damgası imzalanan içeriğin parçasıdır; bu da replay koruması sağlar.
Doğru yapılması gereken iki şey var; aksi hâlde doğrulama üretimde başarısız olur:
v1= değeriyle eşleşmeyi kabul edin. Gizli anahtar rotasyonu
sırasında başlık iki imza taşır ve bunlardan yalnızca birini saklayan bir
ayrıştırıcı, rotasyon penceresi boyunca geçerli teslimatları reddeder.// 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.visitorId}`) console.log(`Bot: ${event.bot?.result}`) // "human" | "bot" | "uncertain"
res.status(200).send("OK")})Şema 2 teslimatları ek olarak X-Tracio-Signature-Ed25519
(t=<unix>,kid=<id>,v1=<base64>) taşır. HMAC gizli anahtarını iki taraf da bilir; bu
nedenle HMAC, gönderenin gizli anahtarı bildiğini kanıtlar ama isteğin TRACIO'dan
geldiğini kanıtlamaz — bunu asimetrik imza yapar. Genel anahtarlar
https://api.tracio.ai/.well-known/webhook-keys adresinde yayımlanır ve kid ile
indekslenir.
Panelden gönderilen test teslimatları yalnızca HMAC ile imzalanır — platformun özel
anahtarı teslimat düğümlerinde yaşar ve bilinçli olarak panele açık değildir.
Ed25519'u katı biçimde zorunlu kılan bir doğrulayıcı, test teslimatlarını geçirmek
zorundadır (bunların eventId değerinde :test soneki bulunur); aksi hâlde üretim
sapasağlamken panelden test yapmak başarısız olur. Aynı dikkat biçim denetimleri için
de geçerlidir: bir test teslimatı requestId değerini test_<hex> biçiminde ve
visitorId olarak birebir test_visitor dizesini taşır; bunları üretim biçimlerine
göre doğrulayan bir handler, aslında kusursuz olan bir teslimatı reddeder.
Rotasyondan sonra her iki gizli anahtar da 24 saat geçerli kalır ve başlık her iki imzayı da taşır; böylece teslimat kaybetmeden yapılandırmanızı güncelleyebilirsiniz. Revoke now eylemi bu pencereyi kısa keser. Gizli anahtarı kendi tarafınızda 24 saat içinde güncelleyin: pencere kapandığında eski anahtar eşleşmeyi bırakır ve uç noktanız geçersiz imzaya 4xx ile yanıt veriyorsa, arka arkaya beş böyle yanıt webhook'u devre dışı bırakır.
| Başlık | Açıklama |
|---|---|
Content-Type | application/json |
X-Tracio-Signature | t=<unix>,v1=<hmac_sha256_hex> — rotasyon penceresinde iki v1= |
X-Tracio-Signature-Ed25519 | Platform imzası, t=<unix>,kid=<id>,v1=<base64> (yalnızca v2) |
X-Tracio-Event-Id | Teslimat tanımlayıcısı — idempotency anahtarı |
X-Tracio-Request-Id | Ziyaret tanımlayıcısı (v2, yalnızca ziyaret olayları) |
X-Tracio-Event-Type | Olay türü (yalnızca v2) |
X-Tracio-Delivery-Attempt | 1'den başlayan deneme numarası (yalnızca v2) |
X-Tracio-Payload-Version | 2 (yalnızca v2) |
X-Tracio-Webhook-Id | Bu teslimatı üreten webhook'un tanımlayıcısı |
Teslimatlar yeniden denenebilir ve yeniden deneme aynı X-Tracio-Event-Id'yi taşır.
Tekilleştirmeyi buna göre yapın:
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")})eventId'nin webhook başına değil, olay başına benzersiz olduğuna dikkat edin:
workspace'teki birden fazla webhook aynı olaya aboneyse, her biri aynı tanımlayıcıya
sahip bir teslimat alır. Bu değer <requestId>:<phase> olarak kurulur; bu yüzden tek
bir ziyaretin üç fazı tek bir kayda çökmek yerine birbirinden bağımsız olarak
tekilleştirilir.
2xx ile yanıt verin — bir teslimatın kabul edildiğinin tek işareti budur.
| Yanıt | Ne olur |
|---|---|
2xx | Teslimat tamamlandı |
429 Too Many Requests | Başarısızlık sayılmaz ve bir denemeyi harcamaz; daha uzun bir Retry-After dikkate alınır |
408, 425, 5xx, kopan bağlantı | Artan bir bekleme ile yeniden denenir |
410 Gone | Uç nokta kaldırılmış sayılır — webhook anında devre dışı bırakılır |
Diğer 4xx | Yeniden denenir, ancak arka arkaya beşi webhook'u devre dışı bırakır — 400/401/404 yeniden denemeyle düzelmez |
Yeniden deneme takvimi: 5 sn → 30 sn → 2 dk → 10 dk → 30 dk → 2 sa → 6 sa (8 deneme). İlk denemeler bir dakikaya sığar; bu yüzden servisinizin kısa bir yeniden başlatması size bir bildirime mal olmaz. Her bekleme, yarım değer ile tam değer arasında rastgele seçilir; böylece bir kesintiden sonra yeniden denemeler tek bir yaylım hâlinde tetiklenmez.
Otomatik devre dışı bırakma hem bir eşik (arka arkaya 20 başarısızlık ya da 5 yapılandırma hatası) hem de en az 15 dakika kesintisiz başarısızlık gerektirir — kuyrukta çok sayıda teslimat birikmiş olsa bile kısa bir yeniden başlatma entegrasyonu öldüremez. 15 dakikadan uzun bir boşluk sayımı sıfırlar. Panel; yanıt kodu ve hata metniyle birlikte nedeni ve sayaçları sıfırlayan bir Re-enable düğmesini gösterir.
| Plan | Workspace başına webhook |
|---|---|
| Free | Kullanılamaz |
| Pro | 5 |
| Business | 20 |
| Enterprise | 100 |
Uç noktalar genel bir IP adresine sahip https olmalıdır — özel ve loopback adresleri,
yönlendirme sırasında dahil olmak üzere reddedilir — ve en fazla iki yönlendirme
derinliğinde olmalıdır.
Yalnızca 307 ve 308 yönlendirmeleri izlenir. 301, 302 ve 303 istemciye
GET'e geçmesini ve gövdeyi bırakmasını söyler; bu nedenle teslimat onları izlemez ve
deneme başarısız sayılır. Yük dengeleyiciniz URL'yi normalleştiriyorsa (www veya
sondaki eğik çizgi ekliyorsa), webhook'u doğrudan nihai URL'ye yönlendirin.
Webhook'lar panelden yönetilir. Panel, uygulama sunucusunda (örneğin
https://app.tracio.ai/api/v1) sunulan workspace kapsamlı bir yönetim API'sini sürer;
aşağıdaki uç noktalar da onun çağırdığı uç noktalardır. Tüm webhook uç noktaları
/workspaces/{wsId} altında bulunur.
Burası sunucudan sunucuya bir yüzey değildir. Yönetim API'si yalnızca panel oturumu JWT'nizi kabul eder ve bunu workspace rolünüze karşı denetler (RBAC);
tracio_sk_…biçimindeki bir gizli anahtar burada reddedilir. Bu oturum tarayıcıda yaşadığı ve onunla birlikte sona erdiği için, aşağıdaki çağrıları otomatikleştirilecek bir entegrasyon gibi değil, panelin ne yaptığının bir tarifi gibi ele alın. Kendi arka ucunuzdan programatik erişim için salt okunur Data API'yi kullanın.
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": [] }'İmzalama gizli anahtarı TRACIO tarafından üretilir ve oluşturmada (ve rotasyonda)
signingSecret altında bir kez döndürülür. Güvenli biçimde saklayın — imzaları
doğrulamak için kullandığınız anahtar odur.
{ "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" }}Sonraki okumalarda signingSecret maskelenir (null) — yalnızca oluşturma ve gizli
anahtar rotasyonu onu açığa çıkarır.
| Yöntem | Yol | Açıklama |
|---|---|---|
GET | /workspaces/{wsId}/webhooks | Webhook'ları listele |
PATCH | /workspaces/{wsId}/webhooks/{webhookId} | url / events / status güncelle |
DELETE | /workspaces/{wsId}/webhooks/{webhookId} | Webhook sil |
POST | /workspaces/{wsId}/webhooks/{webhookId}/test | İmzalı test teslimatı gönder |
POST | /workspaces/{wsId}/webhooks/{webhookId}/secret/rotate | İmzalama gizli anahtarını rotasyona sok |
GET | /workspaces/{wsId}/webhooks/{webhookId}/deliveries | Son teslimat denemelerini listele |
Zaman aşımlarından kaçınmak için mümkün olduğunca hızlı 2xx döndürün ve payload'ı
asenkron biçimde işleyin:
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?.riskScore > 50) { await alertFraudTeam(event) }
if (event.bot?.result === "bot") { await blockVisitor(event.visitorId) }}Uç noktanıza imzalı bir örnek payload göndermek ve onun erişilebilir olduğunu ve
imzaları doğru doğruladığını teyit etmek için bir webhook üzerindeki Test eylemini
(veya POST .../webhooks/{webhookId}/test) kullanın.
Yerel geliştirme için sunucunuzu ngrok gibi bir tünelle dışarı açın:
ngrok http 3000# Use the generated URL as your webhook endpoint