Webhooks stellen Identifikationsereignisse in Echtzeit an Ihren Server zu. Jedes Mal,
wenn ein Besucher identifiziert wird, sendet TRACIO eine HTTP-POST-Anfrage an die
von Ihnen konfigurierte Webhook-URL. Der Anfragekörper ist der Ereignis-Payload.
Sie sind außerdem der einzige Kanal, der späte Verdicts zustellt — jene, bei denen das Verhalten eines Besuchers erst nach dem Laden der Seite bewiesen hat, dass er automatisiert war.
Richten Sie sie im Dashboard unter Settings → Webhooks ein. Webhooks erfordern den Pro-Tarif oder höher.
| Ereignis | Wann | Tarif |
|---|---|---|
identification | Bei jedem Besuch — die Phasen primary, late und correction | Alle |
account_takeover | Das Verhalten unter einem Konto passt nicht mehr zum Profil des Kontoinhabers | Business+ |
attack_detected | Ein Bot-Ausschlag auf Ihrer Website | Business+ |
reputation_changed | Die Reputation der Person hinter einem Gerät hat sich geändert | Business+ |
Ereignisnamen verwenden Unterstriche, niemals Punkte — ein visitor.created oder
session.created gibt es nicht. reputation_changed setzt die Personen-Ebene voraus
und feuert daher nur für Workspaces, in denen die geräteübergreifende
Identitätsauflösung aktiviert ist.
Ein Webhook abonniert bestimmte Typen; der gesonderte Wert * bedeutet „jeder Typ,
einschließlich später hinzugefügter“. Ein unbekannter Typ wird beim Anlegen oder
Bearbeiten eines Abonnements mit 400 abgelehnt, sodass ein Tippfehler Ihnen keinen
Webhook hinterlassen kann, der stillschweigend nie auslöst.
identificationEin einzelner Besuch erzeugt bis zu drei Zustellungen mit derselben requestId:
primary — das erste Verdict, beim Laden der Seite.late — Anreicherung rund neun Sekunden später, sobald die langsamen Prüfungen eingetroffen sind.correction — eine Korrektur auf Basis des Verhaltens (Zeiger, Tastatur, Scrollen).Korrelieren Sie sie über requestId und unterscheiden Sie sie über phase. Die
spätere Phase hat Vorrang: Sagte primary human und correction sagt bot, dann
ist die zweite die richtige Antwort.
Verlassen Sie sich nicht auf die Ankunftsreihenfolge. Jede Phase wird unabhängig
und mit eigenem Retry-Plan zugestellt — ging primary in einen Retry, während late
im ersten Anlauf erfolgreich war, erhalten Sie sie in umgekehrter Reihenfolge.
Bestimmen Sie den Vorrang über das Feld phase, nicht über den Empfangszeitpunkt.
Diese drei sind die einzigen Phasen eines identification-Ereignisses. Ein weiterer
Wert erreicht Sie: account_takeover trägt phase: "beacon", denn ein Alarm über eine
Kontoübernahme wird ausschließlich aus einem Verhaltens-Beacon ausgelöst.
Beachten Sie die Abweichung, die dadurch entsteht, denn sie betrifft die Idempotenz.
Eine produktive identification-Zustellung hat als eventId exakt
<requestId>:<phase>, doch zwei Zustellungen brechen diese Formel. Ein
account_takeover lautet <requestId>:ato — das Suffix ist das Literal ato, nicht
der Wert des Feldes phase. Eine aus dem Dashboard gesendete Test-Zustellung lautet
<requestId>:test, während phase in ihrem Schema-2-Körper weiterhin primary zeigt
— und ein Schema-1-Körper hat überhaupt kein Feld phase, sodass das Suffix
ausschließlich im Header auftaucht. Verwenden Sie eventId direkt als
Idempotenzschlüssel und setzen Sie ihn niemals aus requestId und phase neu
zusammen. Gleichen Sie gegen die Werte ab, die Sie verarbeiten, und ignorieren Sie
alles andere, statt die Zustellung abzulehnen.
attack_detected ist ein Ereignis auf Workspace-Ebene: Es hat weder requestId noch
visitorId und keinen der Blöcke browser, geo, bot oder decision — diese
Schlüssel fehlen schlicht. account_takeover wird von einem konkreten Besuch erzeugt
und trägt den vollständigen Identifikationskörper Ihres Tarifs plus einen
accountAlert-Block. Wenn Sie alle Ereignisse in einem Handler parsen, prüfen Sie
event, bevor Sie Besuchsfelder anfassen.
| Version | Für wen | Wie umstellen |
|---|---|---|
1 | Webhooks, die vor v2 angelegt wurden | Bleibt für sie die Voreinstellung |
2 | Neue Webhooks | Der Schalter auf der Webhook-Karte im Dashboard |
Schema v1 ist eingefroren — keines seiner Felder ändert sich, sodass bestehende Integrationen ohne Anpassungen weiterlaufen. Alles Neue lebt in v2, und genau die geben neue Webhooks aus.
{ "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", "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" }, "bot": { "result": "human", "score": 3.2 }, // human | bot | uncertain "identification": { "confidence": 0.97, "incognito": false }, "decision": { "action": "real", "riskScore": 12.2 } // real | fake | suspicious}Null- und Leerwerte werden weggelassen. String- und Zahlenfelder mit dem Wert null
(zum Beispiel bot.type bei einem Menschen) fehlen im JSON — machen Sie sie in Ihren
Schemata nicht zur Pflicht und lesen Sie verschachtelte Blöcke defensiv.
bot.score und decision.riskScore sind Dezimalwerte auf einer 0..100-Skala mit
einer Nachkommastelle — exakt die Zahlen, die das Dashboard für denselben Besuch
ausweist. (Im eingefrorenen v1-Schema verwenden sie andere Einheiten: einen Bruchteil
0..1 bzw. 0..255.)
bot.type ist entweder der Name eines erkannten Bots oder ein Familien-Label. Siehe
Bot-Typen für das Vokabular — interne Prüfnamen
werden nie offengelegt, in keinem Tarif.
| Feld | Typ | Beschreibung |
|---|---|---|
version | number | Version des Payload-Schemas (2) |
event | string | Ereignistyp |
eventId | string | Kennung der Zustellung — der Idempotenzschlüssel |
requestId | string | Kennung des Besuchs (UUID), geteilt von allen seinen Phasen |
phase | string | primary, late, correction; account_takeover trägt beacon |
visitorId | string | Stabile Besucherkennung |
linkedId | string | Vom Client gelieferte verknüpfte Kennung |
tag | string | Vom Client gelieferter benutzerdefinierter Tag |
timestamp | string | Ereigniszeitpunkt (RFC 3339) |
url | string | URL der Seite, auf der das Ereignis erfasst wurde |
ip | string | IP-Adresse des Clients |
userAgent | string | Roher User-Agent-String des Clients |
browser.name / .version | string | Erkannter Browser |
os.name / .version | string | Erkanntes Betriebssystem |
device | string | Geräteklasse (z. B. desktop, mobile) |
geo | object | IP-Geolokalisierung: country, city, lat, lon, timezone |
network | object | vpn, proxy, tor, datacenter (Booleans) und connectionType |
bot.result | string | human, bot oder uncertain |
bot.type | string | Bot-Name oder Familien-Label, wenn ein Bot erkannt wurde |
bot.score | number | Bot-Score (0–100) |
identification.confidence | number | Identifikations-Konfidenz (0.0–1.0) |
identification.incognito | boolean | Privater/Inkognito-Browsing-Kontext |
decision.action | string | real, fake oder suspicious |
decision.riskScore | number | Aggregierter Risiko-Score (0–100) |
Ab Pro — wie sich der Besucher über die Zeit verhält:
{ "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}Ab Business — warum das Verdict so ausgefallen ist:
{ "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 }, "decision": { "suspectScore": 55 }, "guidance": { "version": 1, "overall": "review" } // see below}Siehe Bot-Erkennung für das Vokabular der
Reason Codes und die Bedeutung von severity.
guidance liefert fertige „Was tun“-Empfehlungen je Integrationspunkt, damit Sie
keine Policy aus Rohwerten ableiten müssen:
{ "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 }}Jedes Szenario startet bei allow und bewegt sich immer nur nach oben auf der
Leiter: allow → challenge → review → deny. Innerhalb eines Szenarios gewinnt
die strengste auslösende Achse, und overall ist der strengste Wert über alle vier
Szenarien.
| Empfehlung | Zahlung | Registrierung | Login | Affiliate |
|---|---|---|---|---|
allow | Verarbeiten | Anlegen | Einlassen | Conversion gutschreiben |
challenge | 3-D Secure / Bestätigung | Captcha, Bestätigung per E-Mail oder Telefon | Step-up-2FA, erneut authentifizieren | Als zweifelhaft markieren, bis Aktivität sichtbar wird |
review | Verarbeiten, aber zur Prüfung einreihen | Mit Einschränkungen anlegen | Einlassen und Alarm auslösen | Auszahlung bis zur Prüfung zurückhalten |
deny | Transaktion nicht verarbeiten | Kontoerstellung ablehnen | Nicht einlassen | Conversion nicht gutschreiben |
version ist die Version des Regelwerks — sie wird erhöht, wenn sich die Logik
verbessert. Guidance ist additiv: Neue Szenarien kommen als neue Schlüssel hinzu, ohne
den Vertrag zu brechen. Die spätere Phase gewinnt, außer bei teilweisen
Empfehlungen: Eine Zustellung, die auf einer unvollständigen Eingabemenge berechnet
wurde, ist mit "partial": true gekennzeichnet, und eine teilweise Empfehlung
überschreibt keine vollständige Empfehlung, die zuvor für dieselbe requestId
eingegangen ist. In einer gewöhnlichen Zustellung fehlt das Feld partial vollständig.
Exakte Schwellenwerte werden bewusst nicht dokumentiert. Eine Empfehlung, die sich zu einem Score zurückrechnen lässt, ist keine Absicherung mehr.
account_takeoverNur Business und Enterprise. Der Körper ist der vollständige Identifikations-Envelope
Ihres Tarifs plus ein accountAlert-Block, höchstens einmal pro Besuch zugestellt:
{ "version": 2, "event": "account_takeover", "eventId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31:ato", "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "phase": "beacon", // der Alarm wird aus einem Beacon ausgelöst; nur die eventId sagt "ato" "accountAlert": { "type": "behavior-drift", "accountId": "user-42", // your linkedId for the account "driftScore": 0.83 } // …the remaining identification fields}In v1 trägt dieser Block type, linkedId und drift; in v2 werden zwei Felder
umbenannt — linkedId → accountId und drift → driftScore. Aktualisieren Sie
Ihren Handler, wenn Sie payloadVersion umstellen, sonst sieht Ihre
Kontoübernahme-Logik die Daten stillschweigend nicht mehr.
attack_detected{ "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 }}Jede Zustellung enthält einen X-Tracio-Signature-Header:
X-Tracio-Signature: t=1710432000,v1=5257a869e7ecebed…t ist der Unix-Zeitstempel (in Sekunden), zu dem die Anfrage signiert wurde.v1 ist der hex-kodierte HMAC-SHA256 von "<t>.<rawRequestBody>", geschlüsselt mit
Ihrem Webhook-Secret.Der Zeitstempel ist Teil des signierten Inhalts, was Replay-Schutz bietet.
Zwei Dinge müssen stimmen, sonst schlägt die Verifizierung in Produktion fehl:
v1=-Wert. Während einer
Secret-Rotation trägt der Header zwei Signaturen, und ein Parser, der nur eine
davon behält, weist während des gesamten Rotationsfensters gültige Zustellungen ab.// 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")})Zustellungen nach Schema 2 tragen zusätzlich X-Tracio-Signature-Ed25519
(t=<unix>,kid=<id>,v1=<base64>). Beide Seiten kennen das HMAC-Secret; der HMAC
beweist also, dass der Absender das Secret kennt, aber nicht, dass die Anfrage von
TRACIO stammt — die asymmetrische Signatur tut das. Die öffentlichen Schlüssel werden
unter https://api.tracio.ai/.well-known/webhook-keys veröffentlicht, indiziert nach
kid.
Test-Zustellungen aus dem Dashboard werden nur mit HMAC signiert — der private
Plattform-Schlüssel liegt auf den Zustellknoten und steht dem Dashboard bewusst nicht
zur Verfügung. Ein Verifizierer, der Ed25519 hart voraussetzt, muss
Test-Zustellungen durchlassen (sie tragen das Suffix :test an der eventId), sonst
scheitern Tests aus dem Dashboard, während die Produktion einwandfrei läuft. Dieselbe
Vorsicht gilt für Formatprüfungen: Eine Test-Zustellung trägt die requestId in der
Form test_<hex> und als visitorId das Literal test_visitor — ein Handler, der
diese gegen die Produktionsformen validiert, weist damit eine ansonsten wohlgeformte
Zustellung ab.
Nach einer Rotation bleiben beide Secrets 24 Stunden lang gültig und der Header trägt beide Signaturen, sodass Sie Ihre Konfiguration ohne Zustellverluste aktualisieren können. Die Aktion Revoke now verkürzt das Fenster. Aktualisieren Sie das Secret auf Ihrer Seite innerhalb von 24 Stunden: Sobald das Fenster schließt, passt das alte Secret nicht mehr, und wenn Ihr Endpunkt auf eine ungültige Signatur mit 4xx antwortet, deaktivieren fünf solche Antworten in Folge den Webhook.
| Header | Beschreibung |
|---|---|
Content-Type | application/json |
X-Tracio-Signature | t=<unix>,v1=<hmac_sha256_hex> — zwei v1= während eines Rotationsfensters |
X-Tracio-Signature-Ed25519 | Plattform-Signatur, t=<unix>,kid=<id>,v1=<base64> (nur v2) |
X-Tracio-Event-Id | Kennung der Zustellung — der Idempotenzschlüssel |
X-Tracio-Request-Id | Kennung des Besuchs (v2, nur Besuchsereignisse) |
X-Tracio-Event-Type | Der Ereignistyp (nur v2) |
X-Tracio-Delivery-Attempt | Versuchsnummer, beginnend bei 1 (nur v2) |
X-Tracio-Payload-Version | 2 (nur v2) |
X-Tracio-Webhook-Id | Kennung des Webhooks, der diese Zustellung erzeugt hat |
Zustellungen können wiederholt werden, und eine Wiederholung trägt dieselbe
X-Tracio-Event-Id. Deduplizieren Sie darüber:
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")})Beachten Sie, dass eventId pro Ereignis eindeutig ist, nicht pro Webhook: Wenn
mehrere Webhooks im Workspace dasselbe Ereignis abonniert haben, erhält jeder eine
Zustellung mit derselben Kennung. Sie wird als <requestId>:<phase> gebildet, weshalb
die drei Phasen eines Besuchs unabhängig voneinander dedupliziert werden, statt zu
einer einzigen zu verschmelzen.
Antworten Sie mit 2xx — das ist das einzige Zeichen, dass eine Zustellung angenommen wurde.
| Antwort | Was passiert |
|---|---|
2xx | Zustellung abgeschlossen |
429 Too Many Requests | Zählt nicht als Fehlschlag und verbraucht keinen Versuch; ein längeres Retry-After wird beachtet |
408, 425, 5xx, abgebrochene Verbindung | Wiederholung mit wachsender Pause |
410 Gone | Der Endpunkt gilt als entfernt — der Webhook wird sofort deaktiviert |
Andere 4xx | Wiederholung, aber fünf in Folge deaktivieren den Webhook — 400/401/404 heilen nicht durch Retry |
Retry-Plan: 5 s → 30 s → 2 min → 10 min → 30 min → 2 h → 6 h (8 Versuche). Die ersten Retries passen in eine Minute, ein kurzer Neustart Ihres Dienstes kostet Sie also keine Benachrichtigung. Jede Pause wird zwischen der Hälfte und dem vollen Wert randomisiert, damit Retries nach einem Ausfall nicht in einer einzigen Salve losgehen.
Die automatische Deaktivierung erfordert sowohl einen Schwellenwert (20 aufeinanderfolgende Fehlschläge oder 5 Konfigurationsfehler) als auch mindestens 15 aufeinanderfolgende Minuten mit Fehlschlägen — ein kurzer Neustart kann die Integration selbst dann nicht killen, wenn viele Zustellungen in der Warteschlange lagen. Eine Lücke von mehr als 15 Minuten setzt die Zählung zurück. Das Dashboard zeigt den Grund mit Antwortcode und Fehlertext sowie eine Schaltfläche Re-enable, die die Zähler zurücksetzt.
| Tarif | Webhooks pro Workspace |
|---|---|
| Free | Nicht verfügbar |
| Pro | 5 |
| Business | 20 |
| Enterprise | 100 |
Endpunkte müssen https mit öffentlicher IP sein — private Adressen und
Loopback-Adressen werden abgelehnt, auch bei einer Weiterleitung — und dürfen nicht
tiefer als zwei Weiterleitungen sein.
Nur 307- und 308-Weiterleitungen werden verfolgt. 301, 302 und 303 weisen
den Client an, auf GET zu wechseln und den Körper zu verwerfen; eine Zustellung folgt
ihnen daher nicht, und der Versuch zählt als fehlgeschlagen. Wenn Ihr Load Balancer die
URL normalisiert (etwa www oder einen abschließenden Schrägstrich ergänzt), richten
Sie den Webhook direkt auf die finale URL.
Webhooks werden im Dashboard verwaltet. Das Dashboard steuert eine
Workspace-scoped Management-API, die auf dem Anwendungs-Host bereitgestellt wird (zum
Beispiel https://app.tracio.ai/api/v1); die Endpunkte unten sind genau die, die es
aufruft. Jeder Webhook-Endpunkt liegt unter /workspaces/{wsId}.
Das ist keine Server-zu-Server-Schnittstelle. Die Management-API akzeptiert ausschließlich das JWT Ihrer Dashboard-Session, geprüft gegen Ihre Workspace-Rolle (RBAC); ein Secret Key
tracio_sk_…wird hier abgelehnt. Da diese Session im Browser lebt und mit ihm abläuft, verstehen Sie die folgenden Aufrufe als Beschreibung dessen, was das Dashboard tut, und nicht als zu automatisierende Integration. Für programmatischen Zugriff aus Ihrem eigenen Backend nutzen Sie die schreibgeschützte Server-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": [] }'Das Signing-Secret wird von TRACIO erzeugt und einmalig beim Anlegen (und beim
Rotieren) unter signingSecret zurückgegeben. Bewahren Sie es sicher auf — es ist der
Schlüssel, mit dem Sie Signaturen verifizieren.
{ "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" }}Bei späteren Lesezugriffen ist das signingSecret maskiert (null) — es wird nur beim
Anlegen und beim Rotieren des Secrets offengelegt.
| Methode | Pfad | Beschreibung |
|---|---|---|
GET | /workspaces/{wsId}/webhooks | Webhooks auflisten |
PATCH | /workspaces/{wsId}/webhooks/{webhookId} | url / events / status aktualisieren |
DELETE | /workspaces/{wsId}/webhooks/{webhookId} | Einen Webhook löschen |
POST | /workspaces/{wsId}/webhooks/{webhookId}/test | Eine signierte Test-Zustellung senden |
POST | /workspaces/{wsId}/webhooks/{webhookId}/secret/rotate | Das Signing-Secret rotieren |
GET | /workspaces/{wsId}/webhooks/{webhookId}/deliveries | Jüngste Zustellversuche auflisten |
Geben Sie so schnell wie möglich ein 2xx zurück und verarbeiten Sie den Payload
asynchron, um Timeouts zu vermeiden:
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) }}Nutzen Sie die Aktion Test an einem Webhook (oder
POST .../webhooks/{webhookId}/test), um einen signierten Beispiel-Payload an Ihren
Endpunkt zu senden und zu bestätigen, dass er erreichbar ist und Signaturen korrekt
verifiziert.
Für die lokale Entwicklung machen Sie Ihren Server über einen Tunnel wie ngrok
erreichbar:
ngrok http 3000# Use the generated URL as your webhook endpoint