Webhook-Events
Case-Lifecycle-Events mit HMAC-SHA256-signierten Payloads abonnieren. Endpoints werden im Dashboard unter Integrations konfiguriert.
Eventkatalog
Heute feuern drei Eventtypen. Neue Events sind nicht-breaking — dein Endpoint erhält nur die Typen, die du abonniert hast, neue müssen explizit aktiviert werden.
| Event | Wann es feuert |
|---|---|
case.completed | Analyse-Pipeline ist für einen Case durchgelaufen (beliebiges Verdikt). |
case.failed | Pipeline ist abgebrochen, bevor ein Verdikt feststand — neuer Versuch oder Re-Upload nötig. |
case.high_fraud_risk | Verdikt LIKELY_MANIPULATED oder LIKELY_AI_GENERATED mit hoher Konfidenz. |
Payload-Envelope
Jedes Event landet in einer stabilen Hülle. Eventspezifische Felder liegen unter data; die Hülle selbst ändert sich nie.
{
"id": "evt_8a1c2d3e-4f5g-6789-abcd-ef0123456789",
"type": "case.completed",
"created": "2026-04-25T18:42:11.482Z",
"data": {
"caseId": "ce_01J9XJ3K4Q9R5S6T7U8V9W0X1Y",
"verdict": "SUSPICIOUS",
"confidence": 62,
"criticalFindings": 0,
"imageCount": 3,
"completedAt": "2026-04-25T18:42:11.391Z"
}
}Signaturprüfung
Jede Auslieferung trägt einen X-Claimscan-Signature-Header — damit lässt sich nachweisen, dass der Request wirklich von Claimscan stammt und unterwegs nicht verändert wurde. Requests ohne valide Signatur müssen abgelehnt werden.
Der Header packt einen Unix-Timestamp und einen hex-codierten HMAC-Digest komma-getrennt zusammen:
X-Claimscan-Signature: t=1714069331,v1=8e2c9f4b1d7a6e3c0b8f5a2d9c1e6b4f3a7d8c2e5f9a1b4c7d0e3f6a9b2c5d8eVerifikation in vier Schritten:
- Header in t (Timestamp) und v1 (Signatur) zerlegen.
- Ablehnen, falls abs(now - t) > 300 Sekunden — Replay-Schutz.
- HMAC-SHA256 über `${t}.${rawBody}` mit dem Endpoint-Secret berechnen. Den Roh-Body nutzen, nicht eine neu serialisierte JSON-Variante.
- Mit v1 in konstanter Zeit vergleichen (timingSafeEqual in Node).
Referenzimplementierung (Node)
import { createHmac, timingSafeEqual } from "node:crypto";
/**
* Verify a Claimscan webhook signature. Reject if:
* - timestamp is older than 5 minutes (replay protection)
* - HMAC-SHA256 over `${timestamp}.${rawBody}` does not match v1
*/
export function verifyClaimscanSignature(opts: {
header: string; // X-Claimscan-Signature
rawBody: string; // request body BEFORE JSON.parse
secret: string; // whsec_<base64url>
}): boolean {
const parts = Object.fromEntries(
opts.header.split(",").map((p) => p.split("="))
);
const t = Number.parseInt(parts.t ?? "", 10);
const v1 = parts.v1;
if (!Number.isFinite(t) || !v1) return false;
const skew = Math.abs(Math.floor(Date.now() / 1000) - t);
if (skew > 5 * 60) return false;
const expected = createHmac("sha256", opts.secret)
.update(`${t}.${opts.rawBody}`)
.digest();
const actual = Buffer.from(v1, "hex");
if (actual.length !== expected.length) return false;
return timingSafeEqual(actual, expected);
}Retry-Policy
Fehlgeschlagene Auslieferungen (Netzwerkfehler oder HTTP 4xx/5xx ausser 410 Gone) werden bis zu vier Mal mit exponentiellem Backoff wiederholt:
| Versuch | Wartezeit nach vorherigem Fehlschlag |
|---|---|
| 1 | Sofort |
| 2 | ≈ 30 Sekunden |
| 3 | ≈ 5 Minuten |
| 4 | ≈ 30 Minuten |
Nach dem vierten Fehlschlag wird die Auslieferung im Dashboard als gescheitert markiert. Im Delivery-Log lässt sich nach einem Endpoint-Fix manuell erneut zustellen.
Sicherheitshinweise
- Webhook-URLs müssen öffentlich per HTTPS erreichbar sein. Private IP-Bereiche, Link-Local- und Metadaten-Adressen werden bei der Registrierung blockiert (SSRF-Guard).
- HTTP-URLs werden abgewiesen. Self-signed Certificates sind in Produktion nicht unterstützt.
- Endpoint-Secret wie einen API-Schlüssel behandeln — nur server-seitig speichern, nie im Client-Code, und im Dashboard rotieren, falls ein Leck vermutet wird.
- Auslieferungen immer als at-least-once behandeln. Die envelope.id eignet sich als Deduplikations-Schlüssel auf deiner Seite.
Endpoints verwalten
Webhook-Endpoints konfigurieren, testen und rotieren unter Integrations → Webhooks im Dashboard.