Webhooks
Ereignisse statt Nachfragen
Ein Abo je Adresse, signierte Zustellungen, Wiederholungen mit wachsendem Abstand. Ereignisse entstehen bei JEDER Änderung — auch wenn sie über ein Portal oder das Dashboard kommt, nicht über diese API.
Ereignis
| Ereignis | Wann es feuert |
|---|---|
| reservation.created | Eine Buchung ist entstanden — über welchen Kanal auch immer. |
| reservation.updated | Eine bestehende Buchung hat sich geändert. |
| reservation.cancelled | Eine Buchung wurde storniert. Eigenes Ereignis, weil darauf anders zu reagieren ist als auf eine Änderung. |
| availability.changed | Der Kalender eines Objekts hat sich geändert (Buchung, Sperre, Freigabe). |
| rate.changed | Ein Tagespreis oder eine Aufenthaltsregel hat sich geändert. |
| task.created | Eine Aufgabe ist entstanden. |
| task.completed | Eine Aufgabe wurde abgeschlossen. |
Abo anlegen
Eine https-Adresse und die Ereignisse, die du willst. Optional nur für bestimmte Objekte. Interne Adressen werden abgelehnt — die Prüfung läuft beim Anlegen UND vor jeder einzelnen Zustellung.
curl -X POST https://open-api.mynextdays.com/v1/webhooks \
-H 'Authorization: Bearer <access_token>' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://meine-anwendung.de/haken/mynextdays",
"events": ["reservation.created", "reservation.cancelled"]
}'Die Antwort enthält das Signier-Secret genau einmal. Jetzt speichern — es wird nie wieder ausgeliefert.
Was ankommt
Schmal gehalten: IDs und die Felder, die die Änderung beschreiben. Den vollen Datensatz holst du bei Bedarf über die API — so bekommt ein Empfänger nichts zugestellt, was er inzwischen nicht mehr sehen dürfte.
{
"id": "b7c9e1a2-…",
"event": "reservation.created",
"created_at": "2026-08-21T09:14:02.418Z",
"property_id": "8f14e45f-…",
"data": {
"reservation_id": "d41d8cd9-…",
"property_id": "8f14e45f-…",
"unit_id": "1c3b5a7d-…",
"status": "confirmed",
"channel": "booking",
"check_in": "2026-09-10",
"check_out": "2026-09-13",
"booked_at": "2026-08-20T18:33:11Z",
"received_at": "2026-08-21T09:14:01Z"
}
}Signatur prüfen
Jede Zustellung trägt einen Header mit Zeitstempel und HMAC-SHA256 über ZEITSTEMPEL.BODY, berechnet mit dem Secret deines Abos. Weil der Zeitstempel mitsigniert ist, erkennst du eine aufgezeichnete und später erneut eingespielte Zustellung — bei einer Signatur nur über den Body ist das nicht möglich.
MyNextDays-Signature: t=<unix-sekunden>,v1=<hmac-sha256>
- Nimm den ROHEN Körper der Anfrage, nicht das geparste und neu serialisierte Objekt — eine andere Byte-Folge ergibt eine andere Signatur. Das ist der häufigste Grund für „Signatur passt nicht“.
- Prüfe das Alter über den Zeitstempel; fünf Minuten Toleranz sind ein guter Wert.
- Vergleiche zeitkonstant (
timingSafeEqual,hmac.compare_digest). Ein normales==macht die Signatur erratbar.
import crypto from "node:crypto";
// WICHTIG: den ROHEN Body verwenden, nicht das geparste Objekt. Ein
// re-serialisiertes JSON hat eine andere Byte-Folge und damit eine andere
// Signatur — der häufigste Grund für „Signatur passt nicht".
export function signaturPruefen(rohBody, header, secret) {
const teile = new Map(
header.split(",").map((t) => t.split("=").map((s) => s.trim())),
);
const t = teile.get("t");
const empfangen = teile.get("v1");
if (!t || !empfangen) return false;
// Alter der Zustellung prüfen: der Zeitstempel ist mitsigniert, damit eine
// aufgezeichnete Zustellung nicht später erneut eingespielt werden kann.
const alterSekunden = Math.abs(Date.now() / 1000 - Number(t));
if (alterSekunden > 300) return false;
const erwartet = crypto
.createHmac("sha256", secret)
.update(`${t}.${rohBody}`)
.digest("hex");
// Zeitkonstanter Vergleich — ein == würde die Signatur erratbar machen.
return crypto.timingSafeEqual(
Buffer.from(erwartet),
Buffer.from(empfangen),
);
}Wiederholungen
Wiederholungen
Antwortet deine Adresse nicht mit 2xx, wird bis zu zehnmal erneut zugestellt — mit wachsendem Abstand von einer Minute bis zu einer Stunde. Antworte schnell und verarbeite danach; ein langsamer Empfänger wird als Fehler gewertet.
Reihenfolge
Nicht garantiert. Nutze created_at des Ereignisses, um eine ältere Zustellung nicht über eine neuere zu schreiben.
Doppelte Zustellung
Ist unwahrscheinlich, aber möglich — behandle die Ereignis-id als Schlüssel und verarbeite jede nur einmal. Diese Zusage gilt für jeden Webhook-Anbieter, nicht nur für diesen.
Nach einem Ausfall nachholen
Waren deine Server weg, holst du die verpassten Ereignisse über GET /v1/events?since=… nach — älteste zuerst, mit denselben ids wie die Zustellung, höchstens 30 Tage zurück. Ein Push allein ist keine Zustellgarantie: irgendwann laufen die Wiederholversuche aus, und ohne diesen Schritt fehlen dir genau die Ereignisse, von denen du nie erfahren hast.