{ }
GET
OpenAPI 3.1 · Webhooks · API Dokumentation · HMAC
Webhooks in OpenAPI 3.1 dokumentieren
Payload-Schemas, Signaturen und Retry-Verhalten

Webhooks wurden jahrelang außerhalb der API-Dokumentation beschrieben – in separaten Markdown-Dateien, die niemand aktuell hält. OpenAPI 3.1 bringt das neue webhooks-Objekt, das Webhook-Payload-Schemas, Sicherheitsanforderungen und Retry-Verhalten direkt in der Spezifikation verankert.

11 Min. Lesezeit OpenAPI 3.1 · webhooks-Objekt · HMAC-SHA256 · Redoc · Swagger UI OpenAPI Specification 3.1.0

1. Das Problem: Webhooks außerhalb der API-Dokumentation

Vor OpenAPI 3.1 war die Dokumentation von Webhooks ein gelöstes Problem ohne Standard. Die typische Lösung: ein separates Markdown-Dokument mit JSON-Beispielen, das manuell synchron mit dem tatsächlichen Webhook-Payload gehalten werden musste. Das Ergebnis in der Praxis: Payload-Schemas driften von der Realität ab, Entwickler verlassen sich auf den eigentlichen Request statt auf die Dokumentation, und jede Änderung am Webhook erfordert Updates an zwei getrennten Stellen.

OpenAPI 3.0 hatte eine inoffizielle Praxis: Webhooks als Callbacks oder als eigene Path-Einträge mit einem Prefix wie /webhooks/order-created zu dokumentieren. Das ist technisch ungenau, weil Webhooks keine API-Endpunkte des Servers sind, sondern HTTP-Anfragen des Servers an den Client. OpenAPI 3.1 löst das sauber mit dem webhooks-Objekt auf derselben Ebene wie paths – eine eigene Sektion für ausgehende Server-zu-Client-Kommunikation.

2. OpenAPI 3.1: Was sich geändert hat

OpenAPI 3.1.0 wurde im Februar 2021 veröffentlicht und bringt mehrere relevante Neuerungen neben dem webhooks-Objekt. Die Spezifikation ist jetzt vollständig JSON-Schema-kompatibel (Draft 2020-12), was bedeutet, dass alle JSON-Schema-Keywords direkt verwendet werden können, ohne Kompatibilitäts-Workarounds. Das ist besonders für Webhook-Payload-Schemas wichtig, wo if/then/else-Bedingungen, unevaluatedProperties und $dynamicRef nutzbar werden.

Weitere 3.1-Neuerungen mit Auswirkung auf Webhook-Dokumentation: pathItems können jetzt in components definiert und per $ref in webhooks und paths wiederverwendet werden. Das erlaubt, gemeinsame Webhook-Strukturen einmal zu definieren, auch wenn mehrere Ereignistypen denselben Base-Payload teilen. Und: license.identifier als SPDX-Identifier ist jetzt offiziell unterstützt – kein großes Feature, aber hilfreich für public API-Spezifikationen.


# openapi.yaml — Top-Level-Struktur mit webhooks in OpenAPI 3.1
openapi: 3.1.0
info:
  title: Mironsoft Order API
  version: 2.0.0
  description: |
    REST API für das Order-Management.

    ## Webhooks
    Diese API sendet Webhook-Events an konfigurierte Endpunkte.
    Alle Events sind unter `webhooks` dokumentiert.
    Signaturprüfung: siehe `X-Mironsoft-Signature` Header.

servers:
  - url: https://api.mironsoft.de/v2

# Webhook-Events auf derselben Ebene wie paths
webhooks:
  order.created:
    post:
      summary: Order Created Event
      description: |
        Wird ausgelöst, wenn eine neue Bestellung erstellt wurde.
        Der Receiver muss HTTP 200 zurückgeben.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderCreatedEvent'
      responses:
        '200':
          description: Webhook erfolgreich empfangen
        '4XX':
          description: Fehler beim Empfang — wird gemäß Retry-Policy erneut gesendet

paths:
  /webhooks/subscriptions:
    post:
      summary: Webhook-Subscription registrieren
      # ...

3. Das webhooks-Objekt: Aufbau und Syntax

Das webhooks-Objekt in OpenAPI 3.1 ist ein Map-Objekt: Schlüssel sind frei wählbare Webhook-Namen (empfohlen: Event-Namen wie order.created, payment.failed), Werte sind Path-Item-Objekte – dieselbe Struktur wie unter paths. Das bedeutet, die gesamte Known-Vocabulary von Path-Items ist verfügbar: HTTP-Methoden, requestBody, responses, parameters und security.

Die HTTP-Methode für Webhooks ist fast immer post. Der requestBody beschreibt den Payload, den der Server an den Consumer sendet. Die responses beschreiben, was der Server vom Consumer erwartet – typischerweise 200 OK als Bestätigung, optional 202 Accepted wenn die Verarbeitung asynchron erfolgt. Diese Perspektivumkehr ist der konzeptionelle Unterschied zu normalen Paths: Im Webhook-Kontext ist der Server der Requester und der Consumer der Responder.

4. Payload-Schemas mit Discriminator

Wenn eine API mehrere Webhook-Event-Typen sendet, die einen gemeinsamen Basis-Payload teilen, ist der OpenAPI-discriminator das richtige Werkzeug. Ein gemeinsames Feld wie event_type oder type unterscheidet die Event-Varianten. Code-Generatoren können daraus typsichere Union-Typen oder sealed classes generieren. Ohne Discriminator müsste der Consumer den Payload manuell parsen und das Typ-Feld auswerten, ohne Schema-Unterstützung.

Die JSON-Schema-Kompatibilität in OpenAPI 3.1 erlaubt auch oneOf mit if/then für komplexere Payload-Strukturen, bei denen der Discriminator-Ansatz nicht ausreicht. Für die meisten Anwendungsfälle ist oneOf mit Discriminator die klarste und tool-freundlichste Variante.


# components/schemas in openapi.yaml
components:
  schemas:

    # Base event schema — shared by all webhook events
    WebhookEvent:
      type: object
      required: [event_type, event_id, occurred_at]
      properties:
        event_type:
          type: string
          description: Discriminator field — identifies the event variant
          example: "order.created"
        event_id:
          type: string
          format: uuid
          description: Unique event ID for idempotency checks
        occurred_at:
          type: string
          format: date-time
        api_version:
          type: string
          example: "2.0.0"

    OrderCreatedEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEvent'
        - type: object
          required: [order]
          properties:
            order:
              $ref: '#/components/schemas/OrderPayload'

    OrderCancelledEvent:
      allOf:
        - $ref: '#/components/schemas/WebhookEvent'
        - type: object
          required: [order, cancellation_reason]
          properties:
            order:
              $ref: '#/components/schemas/OrderPayload'
            cancellation_reason:
              type: string
              enum: [customer_request, inventory, payment_failed, fraud]

    # Discriminated union for all webhook event types
    AnyWebhookEvent:
      oneOf:
        - $ref: '#/components/schemas/OrderCreatedEvent'
        - $ref: '#/components/schemas/OrderCancelledEvent'
      discriminator:
        propertyName: event_type
        mapping:
          "order.created":   '#/components/schemas/OrderCreatedEvent'
          "order.cancelled": '#/components/schemas/OrderCancelledEvent'

5. HMAC-Signatur und Sicherheit dokumentieren

Webhook-Sicherheit ist in der API-Dokumentation oft unterrepräsentiert. Der verbreitetste Ansatz ist eine HMAC-SHA256-Signatur im Header: Der Server signiert den Request-Body mit einem geteilten Secret und schickt die Signatur als Header mit (X-Mironsoft-Signature: sha256=abc123...). Der Consumer verifiziert die Signatur, bevor er den Payload verarbeitet. Ohne diese Verifikation kann jeder beliebige HTTP-Client einen Webhook fälschen.

In OpenAPI 3.1 lässt sich das HMAC-Pattern als eigenes Security-Scheme dokumentieren. Da OAuth2 und API-Keys für Webhooks nicht passend sind, wird ein Custom-Header-Security-Scheme genutzt. Zusätzlich sollte die Dokumentation beschreiben, wie die Signatur berechnet wird: Algorithmus, Header-Format und ein Code-Beispiel in mindestens einer gängigen Sprache. Viele Consumer-Entwickler kopieren dieses Beispiel direkt in ihre Implementierung.


# Security scheme für HMAC-Webhook-Signatur
components:
  securitySchemes:
    webhookSignature:
      type: apiKey
      in: header
      name: X-Mironsoft-Signature
      description: |
        HMAC-SHA256-Signatur des Request-Bodys.

        **Format:** `sha256=<hex-digest>`

        **Verifikation (PHP):**
        ```php
        $signature = hash_hmac('sha256', $rawBody, $webhookSecret);
        $expected  = 'sha256=' . $signature;
        if (!hash_equals($expected, $_SERVER['HTTP_X_MIRONSOFT_SIGNATURE'])) {
            http_response_code(401);
            exit;
        }
        ```

        **Verifikation (Python):**
        ```python
        import hmac, hashlib
        expected = 'sha256=' + hmac.new(
            secret.encode(), body, hashlib.sha256
        ).hexdigest()
        assert hmac.compare_digest(expected, request.headers['X-Mironsoft-Signature'])
        ```

  # Zusätzliche Webhook-spezifische Header
  headers:
    X-Mironsoft-Event-Id:
      description: Unique event ID — use for idempotency
      schema:
        type: string
        format: uuid
    X-Mironsoft-Retry-Attempt:
      description: Retry attempt number (0 = first delivery)
      schema:
        type: integer
        minimum: 0
    X-Mironsoft-Delivery-Timestamp:
      description: Unix timestamp of the delivery attempt
      schema:
        type: integer

# Webhook mit Sicherheitsanforderung
webhooks:
  order.created:
    post:
      security:
        - webhookSignature: []
      # ...

6. Retry-Verhalten und Idempotenz

Webhook-Retry-Verhalten gehört zu den am häufigsten undokumentierten Aspekten von Webhook-Systemen. Wenn der Consumer-Endpunkt nicht erreichbar ist oder einen nicht-2xx-Status zurückgibt, sollte der Server den Webhook erneut senden. Ohne klare Dokumentation des Retry-Verhaltens bauen Consumer-Entwickler keine Idempotenz-Logik ein – und bei einem Retry wird die Bestellung zweimal verarbeitet.

In der OpenAPI-Spezifikation lässt sich das Retry-Verhalten in der Webhook-Beschreibung und in einem eigenen x-webhook-delivery-Extension-Objekt dokumentieren. Das ist kein Standard-Keyword, aber Tools wie Redoc zeigen x--Extensions in der Dokumentation an, wenn sie entsprechend konfiguriert sind. Das Wichtigste ist, dass die Dokumentation die Antwort auf drei Fragen gibt: Wann wird ein Retry ausgelöst, wie viele Retries gibt es, und wie soll der Consumer Idempotenz sicherstellen.

7. Tool-Unterstützung: Redoc, Swagger UI, Stoplight

Redoc unterstützt webhooks ab Version 2.1.0 nativ und zeigt sie als eigene Sektion in der Seitenleiste an. Swagger UI unterstützt Webhooks ab Version 5.0.0 (mit eingeschränkter Darstellung). Stoplight Elements unterstützt OpenAPI 3.1 Webhooks vollständig. API-Clients wie Postman können Webhook-Schemas aus OpenAPI 3.1 importieren, um Consumer-Tests zu schreiben.

Code-Generatoren wie openapi-generator-cli unterstützen Webhook-Schemas für die meisten Zielsprachen seit Version 6.x. Die generierten Klassen können direkt als Payload-Typ im Consumer-Code verwendet werden – inklusive Discriminator-Mapping für Event-Unions. Das reduziert manuelles Schreiben von Parsing-Code erheblich.

Tool OAS 3.1 webhooks Discriminator Code-Gen Ab Version
Redoc Vollständig Ja Nein 2.1.0
Swagger UI Teilweise Teilweise Nein 5.0.0
Stoplight Elements Vollständig Ja Nein 8.0.0
openapi-generator-cli Ja Ja Ja 6.0.0
Postman Import Teilweise Nein 10.x

Mironsoft

OpenAPI 3.1, Webhook-Systeme und API-Dokumentation

OpenAPI 3.1 Webhook-Dokumentation aufsetzen?

Wir migrieren eure API-Spezifikation auf OpenAPI 3.1, dokumentieren Webhooks mit vollständigen Payload-Schemas und HMAC-Sicherheitsdokumentation und integrieren alles in Redoc oder Stoplight Elements.

OAS 3.1 Migration

Migration bestehender openapi.yaml von 3.0 auf 3.1 mit JSON-Schema-Kompatibilität

Webhook-Schemas

Payload-Schemas mit Discriminator, HMAC-Dokumentation und Retry-Beschreibung

Tool-Integration

Redoc, Stoplight Elements oder Swagger UI aufsetzen und in CI-Pipeline integrieren

8. OpenAPI 3.0 vs. 3.1 für Webhooks im Vergleich

Der Unterschied zwischen OpenAPI 3.0 und 3.1 ist für Webhook-Dokumentation besonders relevant. In 3.0 war die beste verfügbare Option, Webhooks als Callbacks eines Subscription-Endpunkts zu dokumentieren oder sie unter eigene paths zu packen – beides konzeptuell ungenau. OpenAPI 3.1 löst das mit dem dedizierten webhooks-Objekt und macht Webhooks zu einem First-Class-Citizen der Spezifikation.

9. Zusammenfassung

OpenAPI 3.1 bringt mit dem webhooks-Objekt die lang fehlende Standardlösung für Webhook-Dokumentation. Payload-Schemas mit oneOf und Discriminator ermöglichen typsichere Consumer-Implementierungen und maschinenlesbare Event-Kataloge. HMAC-Signatur-Dokumentation als Security-Scheme, Retry-Verhalten in der Event-Beschreibung und Tool-Unterstützung in Redoc und Stoplight machen Webhooks zum vollwertigen Teil der API-Spezifikation.

Der wichtigste Schritt: Webhook-Events mit eindeutigen Namen (order.created, payment.failed) und vollständigen Payload-Schemas versehen. Ein event_id-Feld in jedem Payload ermöglicht Idempotenz-Checks auf Consumer-Seite. Und: Retry-Verhalten immer in der Dokumentation beschreiben, damit Consumer-Entwickler von Anfang an Idempotenz-Logik einbauen.

Webhooks in OpenAPI 3.1 — Das Wichtigste auf einen Blick

webhooks-Objekt

Auf derselben Ebene wie paths. Schlüssel: Event-Namen. Werte: Path-Item-Objekte mit requestBody, responses und security.

Payload-Schemas

oneOf + Discriminator für Event-Unions. JSON-Schema-Kompatibilität in OAS 3.1 ermöglicht if/then/else für komplexe Payloads.

HMAC-Signatur

Als apiKey-Security-Scheme dokumentieren. Code-Beispiele für PHP und Python in der description. hash_equals() für timing-sichere Verifikation.

Idempotenz

event_id als UUID in jedem Payload. Consumer speichert verarbeitete IDs. Retry-Verhalten und Retry-Intervalle immer dokumentieren.

10. FAQ: Webhooks in OpenAPI 3.1 dokumentieren

1Ab welcher OpenAPI-Version gibt es webhooks?
Ab OpenAPI 3.1.0 (Februar 2021). In 3.0.x nur Workarounds über callbacks oder paths möglich.
2webhooks vs. paths – was ist der Unterschied?
paths = Server-Endpunkte, die Clients aufrufen. webhooks = HTTP-Requests, die der Server an Consumer sendet. Umgekehrte Perspektive: requestBody ist der Payload des Servers.
3Was ist der Discriminator für Webhooks?
Ein Payload-Feld (z.B. event_type) das Event-Typen unterscheidet. Mit oneOf und discriminator.mapping erzeugen Code-Generatoren typsichere Union-Typen.
4Wie dokumentiere ich HMAC-Signatur?
Als apiKey-Security-Scheme mit in: header. Description enthält Algorithmus, Format und Code-Beispiele für PHP/Python. Im webhook-Eintrag unter security referenzieren.
5Unterstützt Redoc OAS 3.1 webhooks?
Ja, ab Redoc 2.1.0 – eigene Seitenleisten-Sektion, Payload-Schema-Rendering und Sicherheitshinweise.
6Idempotenz bei Webhooks sicherstellen?
event_id (UUID) in jedem Payload. Consumer speichert verarbeitete IDs in Redis mit TTL. Retry liefert dieselbe event_id – Consumer erkennt Duplikat, gibt 200 zurück.
7OAS 3.0 vs. 3.1 für Webhooks?
3.0: kein webhooks-Objekt, nur Workarounds. 3.1: dediziertes webhooks-Objekt, JSON-Schema Draft 2020-12, pathItems in components wiederverwendbar.
8Code-Generierung aus Webhook-Schemas?
openapi-generator-cli ab 6.0.0. Discriminator-Mapping erzeugt typsichere Union-Typen in Java, TypeScript, Kotlin.
9Wie dokumentiere ich Retry-Verhalten?
In der Webhook-description: maximale Retries, Intervalle (exponential backoff), auslösende HTTP-Status-Codes und Deaktivierungs-Schwellenwert.
10Muss der Consumer HTTPS verwenden?
Immer. Webhook-Payloads können sensible Daten enthalten. HMAC-Signatur schützt nicht ohne HTTPS. Als Requirement in der Dokumentation festhalten.