Webhooks
HMAC-SHA256-Signaturen, Delivery-Header und Retry-Verhalten
Registrieren Sie HTTPS-Endpunkte mit POST /webhooks (webhooks:write). Wählen Sie die gewünschten Events und speichern Sie das Signing-Secret sicher — es wird nur bei Erstellung oder Rotation angezeigt.
Event-Typen
| Event | Wann |
|---|---|
order.completed | Bestellung erfolgreich abgeschlossen |
order.cancelled | Bestellung storniert |
ticket.issued | Ticket ausgegeben |
ticket.checked_in | Ticket eingecheckt |
Delivery-Header
Jede Zustellung ist ein POST mit Content-Type: application/json und:
| Header | Zweck |
|---|---|
X-Cowtic-Signature | Hex-HMAC-SHA256 von {timestamp}.{rawBody} |
X-Cowtic-Timestamp | Unix-Zeitstempel (Sekunden) für die Signatur |
X-Cowtic-Event-Id | Stabile Event-ID (Idempotenz für Ihren Empfänger) |
X-Cowtic-Event-Type | Öffentlicher Event-Typ (zum Beispiel order.completed) |
User-Agent | Cowtic-Webhooks/1.0 |
Payload-Envelope
{
"id": "evt_…",
"type": "order.completed",
"createdAt": "2026-07-20T12:00:00.000Z",
"data": {
"orderId": "…"
}
}Signaturen prüfen
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyCowticSignature(params: {
secret: string;
timestamp: string;
rawBody: string;
signature: string;
}): boolean {
const expected = createHmac("sha256", params.secret)
.update(`${params.timestamp}.${params.rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(params.signature, "utf8");
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}Rohbody verwenden
Berechnen Sie den HMAC über die exakten Body-Bytes (als String), die Sie empfangen haben — serialisieren Sie JSON vor der Prüfung nicht neu.
Lehnen Sie Zustellungen mit zu alten Zeitstempeln ab (zum Beispiel älter als fünf Minuten), um Replay-Risiken zu verringern. Deduplizieren Sie mit X-Cowtic-Event-Id.
Retries
Fehlgeschlagene Zustellungen (nicht-2xx oder Netzwerkfehler) werden mit exponential backoff erneut versucht:
| Versuch | Wartezeit bis zum nächsten Versuch |
|---|---|
| 1 | 1 Minute |
| 2 | 2 Minuten |
| 3 | 5 Minuten |
| 4 | 10 Minuten |
| 5 | 20 Minuten |
| 6 | 40 Minuten |
| 7 | 80 Minuten |
| 8 | 160 Minuten |
Nach 8 fehlgeschlagenen Versuchen wird der Outbox-Eintrag als tot markiert. Den Verlauf sehen Sie mit GET /webhooks/{webhookId}/deliveries.
Secrets
- Rotieren mit
POST /webhooks/{webhookId}/rotate-secret. - Aktualisieren Sie Ihren Empfänger vor oder unmittelbar nach der Rotation.
- Antworten Sie schnell mit
2xx, sobald das Event akzeptiert ist (Verarbeitung bei Bedarf asynchron).