Grundlagen
Webhooks
STRYK sendet einen HTTPS-POST an deinen Endpunkt, wenn sich Buchungen, Gäste, Verfügbarkeit, Preise oder Zahlungen ändern. Für Rechnungen gibt es in v1 kein Ereignis; gleiche GET /invoices per updated_since ab. Jede Zustellung ist signiert, wird bei Fehlern wiederholt und steht im Zustellprotokoll.
Endpunkt einrichten
- Endpunkt anlegen
Öffne in STRYK Kanäle → API, Reiter Webhooks, wähle Webhook anlegen, trage eine HTTPS-URL ein und wähle die Ereignisse.
- Signiergeheimnis sichern
STRYK zeigt das Signiergeheimnis (
whsec_…) ein einziges Mal. Bewahre es wie einen API-Schlüssel auf deinem Server auf. - Testereignis senden
Mit Testereignis senden stellt STRYK ein
webhook.test-Ereignis zu. So prüfst du deine Signaturprüfung.
Die URL muss HTTPS verwenden und eine öffentliche Adresse erreichen. STRYK folgt keinen Weiterleitungen und lehnt private oder lokale Ziele ab, beim Speichern und bei jeder Zustellung.
Ereignisse
| Ereignis | Wird gesendet, wenn |
|---|---|
booking.created | eine neue Buchung in STRYK besteht. |
booking.updated | sich eine Buchung ändert: Status, Daten, Zimmer, Gastzuordnung, Notizen. data.changes nennt die Bereiche (status, rooms, guest, details, deleted). |
booking.cancelled | eine Buchung storniert wurde. |
guest.updated | sich Gastkontaktdaten geändert haben. |
availability.changed | sich freie Einheiten oder Restriktionen für Kategorien und einen Zeitraum geändert haben. Änderungen werden je Kategorie und Zeitraum zusammengefasst. |
rate.changed | sich Tagespreise für Kategorien und einen Zeitraum geändert haben, zusammengefasst wie bei der Verfügbarkeit. |
payment.updated | sich der bezahlte Betrag einer Buchung geändert hat; data.bookingId nennt die Buchung. |
webhook.test | Du in STRYK eine Testzustellung angefordert hast. |
unit.updated | unit.updated |
GET /webhook-event-types liefert die aktuelle Liste mit Beschreibungen.
Payload
Ereignisse sind schlank: du enthältst Typ, betroffenes Objekt und dessen ID, aber nicht das ganze Objekt. Den aktuellen Stand liest du über die API. So bleiben personenbezogene Daten aus Logs und Queues heraus, und du handelst nie auf einer veralteten Kopie.
{
"id": "evt_1f6c9e2a-3b4d-4e5f-8a7b-6c5d4e3f2a1b",
"type": "booking.updated",
"apiVersion": "v1",
"createdAt": "2026-11-04T08:20:31.000Z",
"hotelId": "hotel-alpenblick",
"data": { "object": "booking", "id": "9d3c1b2a-7e6f-4a5b-8c9d-0e1f2a3b4c5d", "changes": ["status"] }
}{
"id": "evt_7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d",
"type": "availability.changed",
"apiVersion": "v1",
"createdAt": "2026-11-04T08:21:02.000Z",
"hotelId": "hotel-alpenblick",
"data": {
"object": "availability",
"roomTypeIds": ["0b7e2c1d-5f4a-4c3b-9e8d-7a6b5c4d3e2f"],
"from": "2026-12-01",
"to": "2026-12-04"
}
}Header
POST /webhooks/stryk HTTP/1.1
Content-Type: application/json
User-Agent: STRYK-Webhooks/1.0
STRYK-Event-Id: evt_1f6c9e2a-3b4d-4e5f-8a7b-6c5d4e3f2a1b
STRYK-Event-Type: booking.updated
STRYK-Delivery-Id: 2b1a0f9e-8d7c-4b6a-a594-837261504f3e
STRYK-Delivery-Attempt: 1
STRYK-Signature: t=1793780431,v1=5f2c8a1e9b0d…| Header | Inhalt |
|---|---|
STRYK-Event-Id | Ereignis-ID, identisch mit id im Body. Dasselbe Ereignis behält seine ID bei jedem Versuch. |
STRYK-Event-Type | Ereignistyp, z. B. booking.updated. |
STRYK-Delivery-Id | ID dieser Zustellung an diesen Endpunkt; steht im Zustellprotokoll. |
STRYK-Delivery-Attempt | Nummer des Versuchs, beginnend mit 1. |
STRYK-Signature | t=<Unix-Sekunden>,v1=<HMAC-SHA256 hex>; während einer Secret-Rotation zwei v1-Werte. |
Signatur prüfen
STRYK berechnet HMAC-SHA256(secret, "<t>.<rawBody>") mit deinem Signiergeheimnis als Schlüssel, dem vollständigen angezeigten Wert inklusive whsec_, als UTF-8-Bytes, und sendet das Ergebnis hexadezimal als v1. Prüfe jede Anfrage, bevor du sie verwendest:
- Roh-Body lesen
Verwende genau die empfangenen Bytes. Wer JSON parst und neu serialisiert, ändert Leerzeichen und Reihenfolge, die Signatur passt dann nicht mehr.
- Zeitstempel prüfen
Lehne Anfragen ab, deren
tmehr als 300 Sekunden von deiner Uhr abweicht. Das verhindert das Wiedereinspielen aufgezeichneter Anfragen. - In konstanter Zeit vergleichen
Berechne den HMAC über
t, einen Punkt und den Roh-Body und vergleiche ihn mit jedemv1-Wert in konstanter Zeit. - Ein Treffer genügt
Nach einer Secret-Rotation signiert STRYK 24 Stunden lang mit altem und neuem Secret. Der Header enthält dann zwei
v1-Werte; ein gültiger Treffer reicht.
import crypto from 'node:crypto';
import express from 'express';
export function verifyStrykSignature(rawBody, header, secret, toleranceSeconds = 300) {
if (!header) return false;
let timestamp = null;
const signatures = [];
for (const part of header.split(',')) {
const [key, value] = part.trim().split('=', 2);
if (key === 't') timestamp = Number(value);
if (key === 'v1' && value) signatures.push(value);
}
if (!Number.isInteger(timestamp) || signatures.length === 0) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest();
// During a secret rotation the header carries two v1 values; one match is enough.
return signatures.some((signature) => {
const received = Buffer.from(signature, 'hex');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
});
}
const app = express();
// express.raw keeps the exact bytes. Parsing JSON first would break the signature.
app.post('/webhooks/stryk', express.raw({ type: 'application/json', limit: '1mb' }), (req, res) => {
if (!verifyStrykSignature(req.body, req.get('STRYK-Signature'), process.env.STRYK_WEBHOOK_SECRET)) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(req.body.toString('utf8'));
res.sendStatus(204); // acknowledge within seconds …
queue.add(event); // … and process asynchronously, deduplicated by event.id
});import hashlib
import hmac
import json
import os
import time
from flask import Flask, abort, request
def verify_stryk_signature(raw_body: bytes, header: str | None, secret: str, tolerance: int = 300) -> bool:
if not header:
return False
timestamp, signatures = None, []
for part in header.split(","):
key, _, value = part.strip().partition("=")
if key == "t":
timestamp = value
elif key == "v1" and value:
signatures.append(value)
if not timestamp or not timestamp.isdigit() or not signatures:
return False
if abs(int(time.time()) - int(timestamp)) > tolerance:
return False
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
# During a secret rotation the header carries two v1 values; one match is enough.
return any(hmac.compare_digest(expected, signature) for signature in signatures)
app = Flask(__name__)
@app.post("/webhooks/stryk")
def stryk_webhook():
raw_body = request.get_data() # exact bytes, before any JSON parsing
if not verify_stryk_signature(raw_body, request.headers.get("STRYK-Signature"), os.environ["STRYK_WEBHOOK_SECRET"]):
abort(400)
event = json.loads(raw_body)
enqueue(event) # process asynchronously, deduplicated by event["id"]
return "", 204Antworten und verarbeiten
- Antworte innerhalb von 10 Sekunden mit einem beliebigen
2xx-Status. STRYK verwirft den Antwort-Body. - Erst bestätigen, dann in einer Queue oder einem Hintergrundjob verarbeiten. Langsame Handler führen zu Timeouts und doppelten Zustellungen.
- Verarbeite idempotent je Ereignis-
id: Speicher verarbeitete IDs und überspringe Wiederholungen. - Verlasse dich nicht auf die Reihenfolge. Vergleiche
updatedAtdes gelesenen Objekts mit deinem Stand.
Wiederholungen und automatische Deaktivierung
Eine Zustellung schlägt bei Timeout, Netzwerkfehler oder jedem Status außer 2xx fehl. STRYK wiederholt mit wachsendem Abstand:
| Versuch | Abstand zum vorigen Versuch |
|---|---|
| 1 | sofort |
| 2 | 1 Minute |
| 3 | 5 Minuten |
| 4 | 30 Minuten |
| 5 | 2 Stunden |
| 6 | 6 Stunden |
| 7 | 12 Stunden |
| 8 | 24 Stunden |
Nach dem achten Fehlversuch gilt die Zustellung endgültig als fehlgeschlagen. Scheitert ein Endpunkt 20-mal in Folge über mindestens 24 Stunden ohne einen einzigen Erfolg, deaktiviert STRYK ihn automatisch. Behebe den Fehler und aktiviere den Endpunkt in STRYK wieder.
Zustellprotokoll und manuelle Wiederholung
Unter Kanäle → API → Webhooks zeigt der Bereich Zustellungen eines Webhooks jede Zustellung mit Zeitpunkt, Ereignis, Status, Versuchen, HTTP-Status, nächstem Versuch und Zustellzeit. Mit Erneut senden wiederholst du eine einzelne Zustellung, etwa nach einer Korrektur. Mit Geheimnis erneuern rotierst du dort auch das Signiergeheimnis.