# Was gesendet wird und wie wiederholt wird

Die Anfrage, die Ihr Endpunkt bekommt, die Zustände einer Zustellung, das Wiederholungsschema und die Abschaltung — zum Nachschlagen beim Bau und beim Betrieb eines Empfängers.

> **Typ:** nachschlagen
> **Für wen:** Wer einen Empfänger implementiert oder eine ausgebliebene Zustellung untersucht.
> **Voraussetzung:** Für das Zustell-Log im Portal die Rolle Inhaber oder Manager; für das erneute Zustellen die Rolle Inhaber.

## Die Anfrage an Ihren Endpunkt

Jede Zustellung ist ein POST an die Adresse des Endpunkts. Der Rumpf ist die Nutzlast des Ereignisses als JSON, kompakt serialisiert (ohne Leerzeichen und Zeilenumbrüche). Nur die Nutzlast reist; der Zeitpunkt des Ereignisses, seine Kennung und der Auslöser stehen nicht im Rumpf.

| Bestandteil | Wert |
|---|---|
| Methode | `POST` |
| `Content-Type` | `application/json` |
| `X-Signature` | HMAC-SHA256 über den rohen Rumpf, mit dem Signatur-Geheimnis des Endpunkts, base64url-kodiert |
| `X-Event-Type` | Der Ereignistyp, zum Beispiel `composition.scene.published` |
| `X-Delivery-Id` | Die Nummer der Zustellung (`whd_` und 26 Zeichen); eine Wiederholung trägt dieselbe |
| Rumpf | Die Nutzlast des Ereignisses als JSON — die Felder aus den Ereignistabellen |
| Zeitlimit | 10 Sekunden für Verbindung und Antwort |

So sieht eine Zustellung des Ereignisses `composition.scene.published` aus (Rumpf zur Lesbarkeit umgebrochen; tatsächlich kommt er in einer Zeile):

```http
POST /hooks/signage HTTP/1.1
Host: warenwirtschaft.example.com
Content-Type: application/json
X-Signature: 3q2-7wE5c1nXm0Zk8YbT4vLpQ9sRd6HfJgUaWi2xO7A
X-Event-Type: composition.scene.published
X-Delivery-Id: whd_01KXZACW98SENDNG0000000001

{
  "sceneId": "scn_01KJPX8NH0SZENE00000000001",
  "tenantId": "tnt_01KERPXZM0ADERAPTHEKE00001",
  "sceneVersionId": "scv_01KV5K0JE0VERSN00000000003",
  "versionNumber": 4,
  "contentHash": "b2f80a41d76c395ee0138b5a4c9d27f6103e8ab7592c4df081a63be95d7c024f",
  "appVersionPins": {},
  "publishedById": "usr_01KFB7QPW0TEAM000000000002",
  "at": "2026-07-20T08:31:05.000Z"
}
```

## Die erwartete Antwort

| Ihre Antwort | Wirkung |
|---|---|
| Status 2xx binnen 10 Sekunden | Zustellung gilt als zugestellt; der Fehlerzähler des Endpunkts springt auf null |
| 5xx, 408 oder 429 | Vorübergehender Fehler: Wiederholung nach dem Schema unten |
| Keine Antwort binnen 10 Sekunden, Verbindungsfehler | Wie ein vorübergehender Fehler: Wiederholung |
| Jede andere 4xx | Endgültiger Fehler: keine Wiederholung, Zustellung endgültig gescheitert |

Bei einem Fehlschlag wird der Antworttext gekürzt (höchstens 4 kB) im Zustell-Log gespeichert; bei einer erfolgreichen Zustellung wird nur der Statuscode festgehalten. Ein kurzer, sprechender Text hilft Ihnen später beim Nachsehen; nötig ist er nicht.

## Zustände einer Zustellung

| Zustand in der API | Im Portal | Bedeutung |
|---|---|---|
| `pending` | **Ausstehend** | Versand läuft oder steht unmittelbar bevor |
| `delivered` | **Zugestellt** | Ihr Server hat mit 2xx geantwortet; `deliveredAt` ist gesetzt |
| `failed` | **Fehlgeschlagen** | Vorübergehender Fehler; `nextRetryAt` nennt den nächsten Versuch |
| `dead` | `dead` | Endgültig gescheitert: fünfter Fehlschlag oder eine 4xx-Antwort; `deadAt` ist gesetzt |

Der Weg ist `pending` → `delivered` oder `failed`; aus `failed` führt jeder weitere Versuch wieder über `pending` nach `delivered`, `failed` oder `dead`. Für den Zustand `dead` führt das Portal keine eigene Beschriftung; das Zustell-Log zeigt dort den Wert selbst. Die Beschriftung **Wiederholung läuft** ist im Portal angelegt, aber keinem Zustand zugeordnet.

## Wiederholungsschema

Die Plattform wiederholt nur bei 5xx, 408, 429, Zeitüberschreitung und Netzfehlern. Je Zustellung gibt es höchstens fünf Versuche. Die Wartezeit nach dem n-ten Fehlschlag beträgt 1 Minute · 2ⁿ, mit ±20 % Streuung. Fällige Wiederholungen nimmt ein Takt alle fünf Minuten auf, bis zu 100 je Lauf — die tatsächliche Wartezeit ist deshalb bis zu fünf Minuten länger als der Sollwert.

| Versuch | Nach Fehlschlag warten | Bei erneutem Fehlschlag |
|---|---|---|
| 1 | 2 Minuten ±20 % | Versuch 2 |
| 2 | 4 Minuten ±20 % | Versuch 3 |
| 3 | 8 Minuten ±20 % | Versuch 4 |
| 4 | 16 Minuten ±20 % | Versuch 5 |
| 5 | — | Endgültig gescheitert (`dead`) |

Eine 4xx-Antwort außer 408 und 429 beendet die Zustellung sofort, unabhängig vom Versuchszähler.

## Erneut zustellen von Hand

Im Zustell-Log des Endpunkts steht an jeder Zustellung mit Status **Fehlgeschlagen** die Schaltfläche **Erneut zustellen**. Für eine Zustellung im Zustand `dead` zeigt das Portal keine Schaltfläche; sie stoßen Sie über die API an:

```bash
curl -X POST "$SZENERA_API/v1/webhooks/whk_01KPR20K40WEBHK00000000001/deliveries/whd_01KXZACW98SENDNG0000000001/retry" \
  -H "Authorization: Bearer $TOKEN"
```

Die Antwort (Status 201) ist die Zustellung mit dem Ausgang des neuen Versuchs. Der neue Versuch wird mit dem aktuell gültigen Geheimnis des Endpunkts signiert und trägt dieselbe `X-Delivery-Id`. Ist der Endpunkt inaktiv, wird nichts gesendet; die Antwort trägt die Zustellung unverändert. Die API prüft den Zustand nicht: Auch eine bereits zugestellte Zustellung lässt sich so erneut senden — Ihr Empfänger sieht dann dieselbe Nummer ein zweites Mal.

## Abschaltung eines Endpunkts

Jeder Fehlschlag erhöht den Zähler `consecutiveFailures` des Endpunkts, jeder Erfolg setzt ihn auf null. Liegt der Zähler über 20, wird der Endpunkt inaktiv (`active: false`), und die Plattform verzeichnet `notifications.webhook.endpoint.disabled`. Ein stündlicher Lauf holt nach, was der Zähler beim Versand verfehlt hat. Ein inaktiver Endpunkt bekommt keine neuen Zustellungen; im Portal steht er auf **Inaktiv** mit dem Hinweis „letzter Fehler …“. Wieder einschalten: **Aktivieren** oder `PATCH /v1/webhooks/{id}` mit `{"active": true}`. Das wirkt nicht sofort: Die Plattform merkt sich je Ereignistyp bis zu eine Stunde, ob ein aktiver Endpunkt ihn abonniert. Nach dem Aktivieren oder nach dem Ergänzen von Ereignissen kann es deshalb bis zu eine Stunde dauern, bis die erste Zustellung eines neu abonnierten Typs ankommt; neu angelegte Endpunkte sind sofort wirksam.

## Das Zustell-Log über die API

```bash
curl "$SZENERA_API/v1/webhooks/whk_01KPR20K40WEBHK00000000001/deliveries?limit=50" \
  -H "Authorization: Bearer $TOKEN"
```

Die Antwort ist eine Seite mit `items` und `nextCursor` (wenn es weitergeht). Der nächste Aufruf bekommt `cursor=<nextCursor>`; `limit` liegt zwischen 1 und 200, ohne Angabe bei 50.

```json
{
  "items": [
    {
      "id": "whd_01KXZACW98SENDNG0000000001",
      "tenantId": "tnt_01KERPXZM0ADERAPTHEKE00001",
      "endpointId": "whk_01KPR20K40WEBHK00000000001",
      "eventType": "composition.scene.published",
      "payloadSignature": "3q2-7wE5c1nXm0Zk8YbT4vLpQ9sRd6HfJgUaWi2xO7A",
      "status": "delivered",
      "attemptCount": 1,
      "responseCode": 200,
      "responseBody": null,
      "nextRetryAt": null,
      "dispatchedAt": "2026-07-20T08:31:05.000Z",
      "deliveredAt": "2026-07-20T08:31:06.000Z",
      "deadAt": null,
      "createdAt": "2026-07-20T08:31:05.000Z"
    }
  ],
  "nextCursor": "d2hkOjIwMjYtMDctMTk"
}
```

| Feld | Bedeutung |
|---|---|
| `id` | Nummer der Zustellung — dieselbe wie in `X-Delivery-Id` |
| `endpointId`, `tenantId` | Endpunkt und Organisation |
| `eventType` | Der Ereignistyp |
| `payloadSignature` | Die Signatur, die als `X-Signature` gesendet wurde |
| `status` | `pending`, `delivered`, `failed` oder `dead` |
| `attemptCount` | Zahl der bisherigen Versuche |
| `responseCode`, `responseBody` | Antwort Ihres Servers; `responseCode` ist `null`, wenn keine kam; `responseBody` wird nur bei fehlgeschlagenen Versuchen gefüllt (auf 4 kB gekürzt) — nach einer erfolgreichen Zustellung ist es `null` oder trägt noch den Text des letzten Fehlversuchs |
| `nextRetryAt` | Nächster geplanter Versuch, nur bei `failed` |
| `dispatchedAt`, `deliveredAt`, `deadAt`, `createdAt` | Zeitpunkte des letzten Versands, der Zustellung, des endgültigen Scheiterns und der Anlage |

Der gesendete Rumpf selbst steht nicht in der Liste.

## Rechte

| Aktion | Recht | Rollen |
|---|---|---|
| Zustell-Log lesen | `webhook.delivery.list` | Inhaber, Manager |
| Erneut zustellen | `webhook.delivery.retry` | Inhaber |

Diese Rechte gelten für angemeldete Konten. Ein API-Schlüssel (`ak_…`) kann sie nicht tragen; die Aufrufe brauchen das Zugriffstoken einer Anmeldung, siehe [Anmeldung](/de/api/authentication).

## Weiterführendes

- [Wie wird ein Webhook zugestellt?](/de/webhooks/about-delivery)
- [Die Signatur einer Zustellung prüfen](/de/webhooks/verify-signature)
- [Wenn keine Zustellung ankommt](/de/webhooks/troubleshooting)
