BroadNet
HTTP API & webhooksWebhooks

Webhook-Zustellung und Zuverlässigkeit: Retries, Reihenfolge, Dead-Letter

Aktualisiert am 6 Min. LesezeitStandardsbasiert
Direkte Antwort

Ein webhook ist ein Server-zu-Server-HTTP-Callback: die Messaging-Plattform sendet ein Event per POST an die URL des Consumers. Zuverlässige Zustellung ist at-least-once, daher wiederholen Producer fehlgeschlagene Zustellungen mit exponentiellem Backoff und Jitter und schicken sie nach einem Limit ins Dead-Letter; Consumer müssen idempotent sein, weil Duplikate und Events außer Reihenfolge zu erwarten sind.

DiagrammSo erreicht eine A2P-Nachricht das Telefon
  1. Ihre AnwendungVersand über REST API oder SMPP
  2. BroadNet-PlattformValidiert, routet und fakturiert jede Nachricht
  3. SMSC des BetreibersDirekte Operator-Anbindung
  4. Endgerät des AbonnentenDie Nachricht wird zugestellt
Der Zustellbericht (DLR) wird über denselben Pfad an Ihren Webhook zurückgesendet
§01

Webhook-Architektur

Ein webhook kehrt die üblichen Client/Server-Rollen um: statt dass die Anwendung die Messaging-Plattform nach Aktualisierungen pollt, sendet die Plattform ein Event per POST an eine URL, die die Anwendung registriert hat — eine Änderung des Zustellstatus, eine eingehende Nachricht, ein Kontoereignis. Dies ist die Push-Hälfte des asynchronen Modells, dessen Pull-Hälfte das Pollen von GET /messages/{id} ist. Der Endpoint des Consumers ist ein gewöhnlicher HTTPS-Server, der ein POST annehmen, das Nötigste tun muss, um das Event dauerhaft festzuhalten, und schnell ein 2xx zurückgeben muss. Ein verbreitetes und wichtiges Muster ist, beim Empfang zu quittieren und asynchron zu verarbeiten: der Endpoint schreibt das rohe Event in eine Queue oder ein Log, gibt 200 zurück, und ein separater Worker erledigt die eigentliche Arbeit — sodass langsame Geschäftslogik den Producer nie in ein Timeout und einen Retry treibt.

Ein Delivery-Status-webhook-POST an den Consumerhttp
POST /webhooks/dlr HTTP/1.1
Host: app.example
Content-Type: application/json
Webhook-Id: 8b1a...
Webhook-Timestamp: 1752916522
Webhook-Signature: v1,3f2b...

{
  "event": "message.delivered",
  "message_id": "01HZY8Q3M6",
  "status": "delivered",
  "occurred_at": "2026-07-19T09:15:25Z"
}
§02

Zustellgarantien: at-least-once

Verteilte Zustellung kann nicht gleichzeitig exactly-once und verfügbar sein, daher wählt nahezu jedes produktive Webhook-System at-least-once: der Producer wiederholt, bis er eine Erfolgsquittung sieht, und nimmt in Kauf, dass ein Consumer dasselbe Event gelegentlich mehr als einmal empfängt. Die anderen Extreme sind schlechter — at-most-once verwirft Events beim ersten Fehlschlag, und echtes exactly-once erfordert eine verteilte Koordination, die der HTTP-Transport nicht bietet. At-least-once legt dem Consumer eine Pflicht auf: er muss idempotent sein und anhand eines stabilen Event-Identifiers deduplizieren, sodass ein erneut zugestelltes Event harmlos ist. Der Quittungsvertrag ist einfach: jedes 2xx bedeutet ‚Retry stoppen‘; alles andere (oder ein Timeout) bedeutet ‚später erneut versuchen‘.

§03

Retries mit exponentiellem Backoff und Jitter

Wenn eine Zustellung fehlschlägt oder in ein Timeout läuft, wiederholt der Producer nach einem Zeitplan, der exponentiell wächst — eine Basisverzögerung, die sich mit jedem Versuch bis zu einer Obergrenze verdoppelt — sodass ein kurzzeitig nicht erreichbarer Endpoint nicht überrannt wird. Entscheidend ist, dass die Verzögerung zufälligen Jitter enthält: ohne ihn würden alle Events, die während desselben Ausfalls fehlgeschlagen sind, im Gleichschritt wiederholen und im Moment der Erholung des Endpoints eine synchronisierte Thundering Herd erzeugen. Full Jitter (ein Zufallswert zwischen null und der aktuellen Backoff-Grenze) verteilt diese Last gleichmäßig. Retries laufen weiter, bis das Event erfolgreich ist oder ein Gesamtlimit an Versuchen oder Zeit erreicht ist; danach wird das Event ins Dead-Letter geschickt, statt endlos wiederholt zu werden.

Exponentieller Backoff mit Full Jittertext
base = 2s ; factor = 2 ; cap = 3600s
attempt n bound = min(cap, base * factor^(n-1))
delay(n)        = random(0, bound)      # full jitter

n=1  bound  2s    delay in [0, 2s]
n=2  bound  4s    delay in [0, 4s]
n=3  bound  8s    delay in [0, 8s]
n=4  bound 16s    delay in [0, 16s]
...  until success or attempt/time cap -> dead-letter
§04

Timeouts und Reihenfolge

Ein Producer setzt ein kurzes Timeout pro Zustellung — üblicherweise wenige Sekunden — und behandelt ein Timeout genauso wie einen Fehlschlag: die Verbindung wird abgebrochen und das Event für einen Retry eingeplant. Das ist der mechanische Grund, warum Consumer schnell quittieren und asynchron verarbeiten sollten. Die Reihenfolge ist die schwierigere Garantie. Weil Retries und parallele Zustellung sich verschränken, ist ein naiver Webhook-Strom nur best-effort geordnet: ein wiederholtes früheres Event kann nach einem späteren eintreffen. Systeme, die Reihenfolge brauchen, stellen entweder pro Schlüssel sequenziell zu (alle Events zu einer message id auf einer einzigen geordneten Partition, blockierend bei Fehlern) oder — häufiger — machen das Payload selbstordnend: jedes Event trägt einen Zeitstempel und eine monotone Sequenz, sodass der Consumer eine veraltete Aktualisierung verwerfen kann, statt sich auf die Ankunftsreihenfolge zu verlassen.

Reihenfolge-Strategien für Webhook-Ströme
StrategieWieKosten
Best-effortZustellen, sobald verfügbar; unabhängig wiederholenEreignisse können in falscher Reihenfolge eintreffen
Pro Schlüssel geordnetAlle Ereignisse für eine ID serialisieren; bei Fehler blockierenHead-of-Line-Blocking, geringerer Durchsatz
Selbstordnende PayloadZeitstempel + Sequenz mitführen; Consumer verwirft veralteteConsumer-Logik, aber keine Blockierung
§05

Dead-Letter und Endpoint-Health

Ein Endpoint, der dauerhaft ausfällt, darf nicht für immer Retry-Kapazität verbrauchen. Nachdem die Retry-Policy erschöpft ist, wird das Event in eine Dead-Letter-Queue verschoben — einen dauerhaften Speicher unzustellbarer Events, den ein Operator inspizieren und nach Behebung des Endpoints erneut abspielen kann — statt es still zu verwerfen. Um beide Seiten zu schützen, während ein Endpoint ungesund ist, verfolgen Producer die Zustell-Health pro Endpoint und wenden Circuit-Breaking an: nach einer anhaltenden Serie von Fehlern öffnet der Circuit, und neue Events werden geparkt oder schnell abgewiesen statt versucht; danach testet eine Half-Open-Probe periodisch die Erholung, bevor die normale Zustellung wieder aufgenommen wird. Das verhindert, dass ein toter Consumer einen Rückstau aussichtsloser Versuche erzeugt, und lässt einen sich erholenden Consumer sanft wieder hochfahren. BroadNets konkretes Webhook- und Retry-Verhalten ist für Entwickler dokumentiert.

§06

Einen idempotenten Consumer bauen

Weil at-least-once-Zustellung Duplikate garantiert, verantwortet der Consumer — nicht der Producer — die Exactly-once-Effekte. Das Muster ist ein Dedup-Store, der auf einen stabilen, vom Producer gelieferten Event-Identifier schlüsselt (eine Webhook-Id oder Event-ID, nie allein der Payload-Hash). Beim Empfang prüft der Consumer, ob er diese ID bereits verarbeitet hat; wenn ja, quittiert er mit 2xx und tut sonst nichts; wenn nein, hält er die ID fest und wendet den Effekt in derselben Transaktion an, sodass ein Absturz den Effekt nicht anwenden kann, ohne die ID festzuhalten. In Kombination mit selbstordnenden Payloads macht dies ein erneut zugestelltes oder außer Reihenfolge eintreffendes Event zu einer No-Op — genau das verwandelt einen at-least-once-Transport in exactly-once-Geschäftsergebnisse.

Idempotenter Consumer (Pseudocode)text
on POST(event):
  verify_signature(event)          # reject if invalid
  id = event.headers['Webhook-Id']
  begin transaction:
    if seen(id): return 200        # duplicate -> no-op
    if event.sequence <= last_seq(event.message_id):
        mark_seen(id); return 200  # stale -> discard
    apply_effect(event)
    mark_seen(id); set_last_seq(...)
  commit
  return 200
Häufig gestellte Fragen

Häufig gestellte Fragen

Welche Zustellgarantie bieten webhooks?

At-least-once. Der Producer wiederholt, bis er eine 2xx-Quittung sieht, daher kann ein Consumer dasselbe Event mehr als einmal empfangen. Exactly-once über einfaches HTTP würde eine verteilte Koordination erfordern, die der Transport nicht bietet, daher wird erwartet, dass der Consumer idempotent ist und anhand einer stabilen Event-ID dedupliziert.

Warum Jitter zum Webhook-Retry-Backoff hinzufügen?

Ohne Jitter wiederholt jedes Event, das während eines Ausfalls fehlschlug, im Gleichschritt, sobald der Endpoint sich erholt, und erzeugt eine synchronisierte Thundering Herd, die ihn erneut umwerfen kann. Full Jitter — eine zufällige Verzögerung zwischen null und der aktuellen Backoff-Grenze — verteilt die Retries gleichmäßig, sodass der sich erholende Endpoint einen allmählichen Anstieg sieht.

Wie sollte ein Webhook-Endpoint antworten?

Schnell und mit einem 2xx. Das empfohlene Muster ist, das rohe Event dauerhaft festzuhalten, sofort 200 zurückzugeben und asynchron in einem separaten Worker zu verarbeiten. So löst langsame Geschäftslogik nie das Zustell-Timeout des Producers aus und triggert keinen unnötigen Retry.

Werden Webhook-Events in Reihenfolge zugestellt?

Standardmäßig nur best-effort — Retries und parallele Zustellung können sich verschränken, sodass ein früheres Event nach einem späteren eintreffen kann. Für Reihenfolge serialisiert man entweder pro Schlüssel (um den Preis von Head-of-Line-Blocking) oder macht das Payload selbstordnend mit Zeitstempel und Sequenznummer, sodass der Consumer veraltete Aktualisierungen verwirft.

Was ist eine Dead-Letter-Queue für webhooks?

Ein dauerhafter Speicher für Events, die nach Erschöpfung der Retry-Policy nicht zugestellt werden konnten. Statt sie still zu verwerfen, parkt der Producer sie, sodass ein Operator die Fehlschläge inspizieren und die Events nach Behebung des Consumer-Endpoints erneut abspielen kann.

Warum Circuit-Breaking auf Webhook-Endpoints anwenden?

Ein dauerhaft fehlschlagender Endpoint würde sonst Retry-Kapazität mit aussichtslosen Versuchen verbrauchen und einen Rückstau aufbauen. Circuit-Breaking öffnet nach anhaltenden Fehlern, parkt oder verwirft neue Events schnell und nutzt dann eine Half-Open-Probe, um die Erholung zu testen, bevor die normale Zustellung wieder aufgenommen wird — das schützt sowohl den Producer als auch den sich erholenden Consumer.

Referenzen

Quellenspezifikationen

Diese Seite basiert auf den veröffentlichten Standards. (Wird in einem neuen Tab geöffnet.)

BNWird vom BroadNet -Entwicklungsteam betreut.·Messaging in Carrier-Qualität seit 2003
Hänge es an den Draht

Entwicklung mit echtem SMPP Binding und REST-API.

Fordern Sie Testzugriff mit echten Testschlüsseln an: Senden Sie zunächst an Ihre eigene verifizierte Nummer und gehen Sie dann in über 200 Ländern zum Produktivbetrieb über, sobald Ihre Absenderidentität und die Routen genehmigt wurden.