200 OK ist keine Geschäftsbestätigung: Webhooks zuverlässig verarbeiten
Retries, Duplikate, falsche Reihenfolge und stille Worker-Fehler machen Webhooks fragil. So prüfen Sie Idempotenz, Queues, Signaturen und Reconciliation.
Ein Kunde bezahlt. Stripe meldet den Erfolg per Webhook. Ihr Endpoint antwortet mit 200 OK. Im Dashboard steht die Lieferung auf erfolgreich.
Trotzdem wurde das Kundenkonto nicht freigeschaltet.
Oder schlimmer: Es wurde zweimal freigeschaltet, zweimal eine Rechnung erzeugt und zweimal eine interne Benachrichtigung verschickt.
Webhooks wirken auf den ersten Blick wie normale API-Requests. In Produktion sind sie aber ein verteiltes Nachrichtensystem mit Netzwerkfehlern, Wiederholungen, möglichen Duplikaten, zeitversetzten Zustellungen und providerabhängigem Retry-Verhalten. Genau deshalb ist „Der Endpoint antwortet mit 200“ keine ausreichende Aussage darüber, ob der Geschäftsprozess zuverlässig funktioniert.
Zustellung und Verarbeitung sind zwei verschiedene Dinge
Ein Webhook-Provider möchte zunächst wissen, ob Ihre Anwendung die Nachricht angenommen hat. Ihre Anwendung muss danach noch entscheiden, ob und wie der fachliche Vorgang verarbeitet wird.
Ein robuster Ablauf trennt deshalb diese Schritte:
- Request empfangen.
- Herkunft und Signatur prüfen.
- Ereignis identifizieren.
- Ereignis dauerhaft speichern oder sicher in eine Queue legen.
- Dem Provider zügig antworten.
- Fachliche Verarbeitung durchführen.
- Ergebnis und Fehlerzustand dokumentieren.
- Bei Bedarf erneut verarbeiten oder mit der Quelle abgleichen.
Wenn Schritt 5 erfolgreich ist, Schritt 6 aber später scheitert, darf das Ereignis nicht einfach verschwinden. Wenn Schritt 4 nicht dauerhaft war und der Prozess direkt nach dem 200 abstürzt, haben Sie dem Provider Erfolg gemeldet, obwohl intern nichts mehr übrig ist, das verarbeitet werden könnte.
Das ist der Unterschied zwischen Transport-Erfolg und Business-Erfolg.
Provider verhalten sich nicht identisch
Eine gefährliche Annahme lautet: „Webhooks werden bei Fehlern sowieso erneut gesendet.“
Das stimmt nicht universell. Stripe dokumentiert für Live-Mode automatische Wiederholungen über bis zu drei Tage mit exponentiellem Backoff. Stripe weist außerdem ausdrücklich darauf hin, dass Events mehrfach eintreffen können und nicht garantiert in der Reihenfolge zugestellt werden, in der sie entstanden sind.
GitHub verhält sich anders: GitHub dokumentiert, dass fehlgeschlagene Webhook-Lieferungen nicht automatisch erneut zugestellt werden. Sie können manuell oder über einen eigenen automatisierten Prozess erneut ausgeliefert werden. GitHub empfiehlt außerdem, innerhalb von zehn Sekunden mit einem 2xx zu antworten und längere Arbeit asynchron zu verarbeiten.
Damit folgt eine zentrale Betriebsregel: Retry-, Ordering- und Redelivery-Verhalten gehört zur Integration selbst und muss pro Anbieter dokumentiert sein.
Red Flag 1: Der Handler ist nicht idempotent
Stellen Sie sich vor, ein Event payment.succeeded wird zweimal geliefert. Beim ersten Mal setzt die Anwendung die Bestellung auf bezahlt, erzeugt eine Rechnung, bucht eine Provision, schaltet Zugang frei und sendet eine E-Mail. Beim zweiten Mal passiert exakt dasselbe noch einmal.
Dann hat der Transport möglicherweise korrekt wiederholt, aber die Anwendung ist nicht robust gegen Wiederholung. RFC 9110 beschreibt Idempotenz als Eigenschaft, bei der mehrere identische Requests dieselbe beabsichtigte Wirkung haben wie ein einzelner Request. Ein Webhook-POST ist nicht automatisch idempotent. Diese Eigenschaft muss auf Anwendungsebene hergestellt werden.
Typische Schutzmechanismen sind:
- Provider-Event-ID eindeutig speichern,
- Unique Constraint auf die Event-ID setzen,
- fachliche Objekt-ID plus Event-Typ berücksichtigen, wenn ein Anbieter mehrere Events für denselben Vorgang erzeugt,
- Statusübergänge so implementieren, dass „bereits bezahlt“ dieselben Side Effects nicht erneut auslöst,
- externe Folgeaktionen ebenfalls mit Idempotenzschlüsseln oder eindeutigen Geschäftsreferenzen absichern.
Das Ziel ist nicht, jede doppelte Lieferung zu verhindern. Das Ziel ist, dass eine doppelte Lieferung keine doppelte fachliche Wirkung erzeugt.
Red Flag 2: Die Anwendung vertraut auf Event-Reihenfolge
Ein Abo-System erwartet: Subscription erstellt, Rechnung erstellt, Zahlung erfolgreich, Zugang aktivieren. Dann trifft invoice.paid vor customer.subscription.created ein.
Stripe dokumentiert ausdrücklich, dass Event-Reihenfolge nicht garantiert ist. Ein Handler, der voraussetzt, dass vorherige Events bereits verarbeitet wurden, kann deshalb zufällig funktionieren und unter Last oder bei Retry-Situationen brechen.
Robuster ist:
- das Event als Hinweis auf einen Zustand behandeln,
- fehlende aktuelle Objekte bei Bedarf über die Provider-API nachladen,
- Statusübergänge gegen den aktuellen fachlichen Zustand prüfen,
- abhängige Verarbeitung über eine eigene State Machine oder Queue ordnen,
- keine kritische Wahrheit nur aus der Reihenfolge eingehender Webhooks ableiten.
Ein Webhook sagt häufig: „Etwas ist passiert.“ Für die Frage „Wie sieht der aktuelle Zustand aus?“ ist die API des Quellsystems oft die bessere Autorität.
Red Flag 3: 200 OK wird vor einer dauerhaften Annahme gesendet
Schnell zu antworten ist richtig. Zu früh zu antworten ist gefährlich.
Ein problematischer Ablauf sieht so aus: Signatur prüfen, 200 OK senden, Event in Memory an einen Worker übergeben, Prozess crasht. Der Provider glaubt, die Lieferung sei abgeschlossen. Ihre Anwendung besitzt das Event nicht mehr.
Besser ist:
- Signatur und grundlegende Form prüfen.
- Event-ID und relevante Metadaten dauerhaft persistieren oder erfolgreich in eine belastbare Queue schreiben.
- Erst danach
2xxzurückgeben. - Fachliche Verarbeitung asynchron durchführen.
Damit wird aus dem Endpoint ein zuverlässiger Eingang statt eines Hoffnungskorridors zwischen HTTP-Request und Hintergrundprozess.
Red Flag 4: Zu viel Arbeit passiert im Request
Die Gegenrichtung ist ebenfalls problematisch: Der Webhook-Request wartet auf PDF-Erstellung, CRM-Sync, E-Mail-Versand und drei externe APIs.
GitHub empfiehlt eine Antwort innerhalb von zehn Sekunden. Stripe empfiehlt ebenfalls, vor komplexer Logik schnell mit einem erfolgreichen Status zu antworten und die Verarbeitung asynchron zu gestalten.
Lange synchrone Handler verursachen Timeouts, unnötige Wiederholungen, parallele Verarbeitung desselben Events und schwer unterscheidbare Fehler zwischen Transport und Business-Logik. Die robuste Grenze lautet: Im HTTP-Pfad nur das tun, was nötig ist, um die Nachricht sicher anzunehmen.
Red Flag 5: Signaturprüfung funktioniert nur im Happy Path
Webhook-Secrets sind keine Dekoration. Ein öffentlich erreichbarer Endpoint kann sonst von beliebigen Dritten mit selbstgebauten Payloads angesprochen werden.
Stripe weist darauf hin, dass für die Signaturprüfung der unveränderte Raw Body benötigt wird. Wenn ein Framework JSON bereits parsed, normalisiert oder verändert, kann eine eigentlich korrekte Signaturprüfung fehlschlagen. GitHub empfiehlt ebenfalls Webhook-Secrets und HTTPS.
Ein Security-Check sollte mindestens prüfen:
- wird jede relevante Lieferung signaturgeprüft,
- wird der unveränderte Request Body verwendet, wenn der Provider das verlangt,
- werden Secrets getrennt pro Umgebung und Endpoint geführt,
- können alte Secrets kontrolliert rotiert werden,
- werden Timestamps beziehungsweise Replay-Schutz des Anbieters korrekt berücksichtigt,
- landen Signaturen oder Secrets nicht in Logs,
- werden unerwartete Event-Typen bewusst abgelehnt oder ignoriert.
Eine gültige Signatur beweist die Herkunft. Sie beweist nicht, dass die fachliche Verarbeitung doppelt sicher, in der richtigen Reihenfolge oder fehlerfrei ist.
Red Flag 6: Fehlerzustände sind nur Logzeilen
Der Worker scheitert bei einer CRM-API mit 500. Im Log steht ein Stack Trace. Danach passiert nichts. Das ist kein Retry-System.
Für geschäftskritische Webhooks braucht es einen nachvollziehbaren Zustandsautomaten, zum Beispiel received, processing, processed, retry_scheduled, failed, dead_letter und reconciled. Dazu gehören Anzahl der Versuche, letzter Fehler, nächster Retry-Zeitpunkt, Event-Typ, Provider-Event-ID, fachliche Referenz, Release-Version und Abschlusszeitpunkt.
So kann ein Team unterscheiden zwischen „nie angekommen“, „angenommen, aber fehlgeschlagen“ und „erfolgreich abgeschlossen“.
Red Flag 7: Retries wiederholen destruktive Side Effects
Ein Retry darf nicht jedes Mal erneut eine Rechnung anlegen, eine Mail senden, eine Gutschrift buchen, Bestand reduzieren, einen Account provisionieren oder ein Ticket erzeugen.
Idempotenz sollte deshalb nicht nur auf dem Webhook-Event liegen, sondern auch auf den nachgelagerten Aktionen. Ein gutes Muster bindet Side Effects an stabile Geschäftskennungen. Eine Rechnung erhält beispielsweise eine eindeutige Referenz zum Payment oder Auftrag. Eine Provisionierung referenziert denselben Vertrag. Ein E-Mail-Job kann pro Template und Business-Event dedupliziert werden.
So bleibt ein technischer Retry technisch – und wird nicht zur wiederholten Geschäftsaktion.
Red Flag 8: Es gibt keinen Reconciliation-Prozess
Auch ein sauber gebautes Webhook-System kann Ereignisse verpassen. GitHub dokumentiert beispielsweise, dass fehlgeschlagene Lieferungen nicht automatisch erneut zugestellt werden. Stripe bietet automatische Wiederholungen und zusätzlich Wege, nicht zugestellte Events nachzuverarbeiten. Trotzdem sollte ein kritischer Prozess nicht ausschließlich darauf vertrauen, dass jede einzelne Push-Nachricht perfekt ankommt.
Reconciliation bedeutet: Die Anwendung vergleicht regelmäßig ihren Zustand mit der Quelle. Beispiele sind Zahlungen beim Payment-Provider gegen lokale Bestellungen zu prüfen, aktive Subscriptions gegen lokale Zugangsrechte abzugleichen, fehlende GitHub-Events anhand Delivery-Historie zu erkennen oder CRM-Synchronisationen anhand von Zeitstempeln und Cursorn nachzuziehen.
Ein Webhook liefert Geschwindigkeit. Reconciliation liefert Vollständigkeitskontrolle.
Ein belastbares Webhook-Muster
Für viele Integrationen funktioniert dieses Grundmodell gut:
1. Eingangsschicht
HTTPS, Signaturprüfung, Payload-Größenlimit, erlaubte Event-Typen, Provider-Event-ID extrahieren und Payload kontrolliert speichern.
2. Deduplizierung
Unique Constraint auf Event-ID, bei doppeltem Event kontrolliert 2xx antworten und keine zweite Business-Verarbeitung starten.
3. Durable Queue
Event oder interne Job-ID dauerhaft enqueuen, Queue-Fehler nicht als erfolgreichen Empfang bestätigen und Backoff sowie maximale Versuche definieren.
4. Worker
Aktuellen Business-Zustand laden, fehlende Provider-Daten nachladen, idempotente Statusübergänge ausführen, Side Effects deduplizieren und Ergebnis speichern.
5. Dead-Letter und Eskalation
Wiederholt fehlerhafte Events sichtbar ablegen, nach fachlicher Kritikalität alarmieren, manuelle Wiederholung ermöglichen und Fehlerursache dokumentieren.
6. Reconciliation
Kritische Quellsysteme regelmäßig abgleichen, verpasste oder inkonsistente Zustände erkennen und einen Reparaturpfad ohne manuelle Datenbankmagie bereitstellen.
Sieben Tests, die mehr sagen als „Webhook kommt an“
- Dasselbe Event zweimal senden. Es darf keine doppelte fachliche Wirkung geben.
- Events in falscher Reihenfolge senden. Der Endzustand muss korrekt bleiben.
- Worker nach Annahme abstürzen lassen. Das Event muss wieder aufgenommen werden können.
- Queue beim Empfang ausfallen lassen. Der Endpoint darf keinen falschen Erfolg melden.
- Ungültige Signatur senden. Es darf keine Business-Verarbeitung starten.
- Externe Folge-API temporär mit
500antworten lassen. Retry und Dead-Letter müssen nachvollziehbar funktionieren. - Ein Event bewusst auslassen. Reconciliation muss die Abweichung später finden können.
Wer diese Fälle automatisiert testet, prüft die echte Zuverlässigkeit der Integration und nicht nur den Happy Path.
Was Website-Pflichtencheck prüfen würde
Ein technischer Webhook- und Integrationscheck kann öffentlich erreichbare Webhook-Endpunkte, HTTPS, Secrets und Signaturvalidierung, Raw-Body-Handling, Deduplizierung und Unique Constraints, provider-spezifisches Retry-Verhalten, asynchrone Verarbeitung und Queue-Durability, Event-Reihenfolge und State Machines, idempotente Side Effects, Dead-Letter-Queues, Monitoring, manuelle Redelivery, Reconciliation und Regressionstests für doppelte, verspätete und fehlende Events untersuchen.
Das Ziel ist nicht, jede Integration unnötig kompliziert zu machen. Ein Kontaktformular braucht vielleicht keinen Event-Bus. Ein Zahlungs-, Provisionierungs-, Account- oder Vertragsprozess braucht aber mehr als einen Endpoint, der gelegentlich 200 sagt.
Ein erfolgreicher Webhook ist nicht der Request, der angekommen ist. Es ist der Geschäftsprozess, der trotz Retry, Duplikat, falscher Reihenfolge und temporärem Ausfall am Ende genau einmal die richtige Wirkung erzeugt.