> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks – Übersicht

> Wie ThunderPhone Echtzeitereignisse übermittelt, wie Sie Signaturen verifizieren und wie sich die Legacy- und die endpunktbasierten Übermittlungsmodelle unterscheiden.

ThunderPhone sendet HTTP-`POST`-Anfragen an Ihren Server, wenn während eines Anrufs Ereignisse
auftreten — ein eingehender Anruf beginnt, ein Anruf endet, ein Bewertungsdurchlauf
abgeschlossen wird, eine Warnung ausgelöst wird usw. Es gibt **zwei Zustellmodelle**:

<CardGroup cols={2}>
  <Card title="Webhook-Endpunkte (empfohlen)" icon="bolt" href="/de/webhooks/endpoints">
    Mehrere URLs, Secrets pro Endpunkt, Ereignisfilter pro Endpunkt
    und automatische Wiederholungsversuche.
    Verwaltung über `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>

  <Card title="Legacy-Webhook mit einzelner URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Eine URL pro Organisation. Enthält die Ereignisse des Anruflebenszyklus, einschließlich der
    **blockierenden** Konfigurationsaustausche. Verwaltung über `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Alle zehn Ereignistypen im [Ereigniskatalog](/de/webhooks/events) werden
über Webhook-Endpunkte zugestellt. Die sechs Ereignisse des Anruflebenszyklus
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) werden **zusätzlich** an den
Legacy-Webhook mit einzelner URL gesendet — wenn Sie sowohl eine Legacy-URL als auch einen
passenden Endpunkt haben, erhalten Sie das Ereignis über **beide** Pfade. Das blockierende
Verhalten (der [`telephony.incoming`- / `web.incoming`-Konfigurationsaustausch](/de/webhooks/call-incoming)
und der [Tool-Versand](/de/tools/overview) im Webhook-Modus) ist
ausschließlich auf dem Legacy-Pfad verfügbar; jede Zustellung an einen Endpunkt ist eine
Fire-and-Forget-Benachrichtigung.

## Payload-Format

Endpunktzustellungen sind ein JSON-Objekt mit `data`, `event_id` und
`type`:

```json theme={null}
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

`event_id` ist für jedes ausgegebene Ereignis eindeutig. Sie ist bei Wiederholungsversuchen
**und** über jeden Endpunkt hinweg, der das Ereignis empfängt, identisch — verwenden Sie sie zur Deduplizierung.

Der Legacy-Webhook mit einzelner URL sendet denselben `type` und dieselben `data`, jedoch
**ohne** `event_id`:

```json theme={null}
{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
```

Bei der Übertragung wird jeder Body kanonisch serialisiert — Schlüssel alphabetisch sortiert,
ohne Leerraum, UTF-8. Die formatierten Beispiele in diesen
Docs dienen nur der Lesbarkeit.

Im [Ereigniskatalog](/de/webhooks/events) finden Sie die vollständige Liste der Ereignistypen
und Payload-Felder.

## Signaturprüfung

Jede Anfrage enthält im Header `X-ThunderPhone-Signature` eine HMAC-SHA256-Signatur über den **rohen Anfrage-Body**. Der Signaturschlüssel ist das `secret` des Endpunkts (oder Ihr Webhook-`secret` auf Organisationsebene für Legacy-Zustellungen).

### Schritte

1. Lesen Sie den rohen Anfrage-Body **vor** jeder Verarbeitung.
2. Berechnen Sie `hmac_sha256(secret, body).hexdigest()`.
3. Vergleichen Sie das Ergebnis in konstanter Zeit mit dem Header `X-ThunderPhone-Signature`.

Wir signieren exakt die Bytes, die wir übertragen. Diese Bytes sind die kanonische JSON-Serialisierung (sortierte Schlüssel, kompakte Trennzeichen). Daher funktioniert die Prüfung anhand des rohen Bodys immer — und wenn Ihr Framework Ihnen nur geparstes JSON bereitstellt, erzeugt eine erneute Serialisierung mit sortierten Schlüsseln und kompakten Trennzeichen identische Bytes. Beide Vorgehensweisen werden im [Leitfaden zur Signaturprüfung](/de/guides/verify-webhook-signatures) behandelt.

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode("utf-8"),
          body,
          hashlib.sha256,
      ).hexdigest()
      return hmac.compare_digest(expected, signature or "")

  # Example Flask handler
  from flask import Flask, request, abort
  app = Flask(__name__)

  @app.post("/thunderphone-webhook")
  def handle():
      body = request.get_data()
      sig = request.headers.get("X-ThunderPhone-Signature", "")
      if not verify_signature(body, sig, WEBHOOK_SECRET):
          abort(401)
      event = request.get_json()
      # dispatch on event["type"] …
      return "", 204
  ```

  ```javascript Node.js (Express) theme={null}
  import crypto from "node:crypto";
  import express from "express";

  function verifySignature(body, signature, secret) {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(body)
      .digest("hex");
    if (!signature || expected.length !== signature.length) return false;
    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(signature),
    );
  }

  const app = express();
  app.post(
    "/thunderphone-webhook",
    express.raw({ type: "application/json" }),
    (req, res) => {
      const sig = req.header("X-ThunderPhone-Signature") || "";
      if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
        return res.sendStatus(401);
      }
      const event = JSON.parse(req.body.toString("utf8"));
      // dispatch on event.type …
      res.sendStatus(204);
    },
  );
  ```
</CodeGroup>

## Zustellungssemantik

Diese Semantik gilt für Zustellungen an **Endpunkte**. Der ältere Webhook mit einer einzelnen URL führt einen einzelnen synchronen Versuch ohne Wiederholungen aus.

<AccordionGroup>
  <Accordion title="Wiederholungen">
    Jedes Ereignis wird sofort einmal zugestellt. Jede `2xx`-Antwort
    bestätigt die Zustellung. Bei jedem anderen Ergebnis (kein 2xx,
    Verbindungsfehler, Zeitüberschreitung) wiederholen wir den Versuch
    **1 m, 5 m, 30 m, 2 h, 6 h, 12 h und 24 h nach dem ersten Versuch** —
    8 Versuche über 24 Stunden. Wenn jeder Versuch fehlschlägt, wird die
    Zustellung beendet und der Endpunkt in
    [Webhook-Endpunkten](/de/webhooks/endpoints) mit `status="failing"`
    markiert. Geben Sie `2xx` zurück, sobald die Nutzlast dauerhaft
    angenommen wurde; verarbeiten Sie sie asynchron.
  </Accordion>

  <Accordion title="Reihenfolge">
    Die Zustellungsreihenfolge erfolgt nach bestem Bemühen. In der Praxis
    stellen wir Ereignisse in der Reihenfolge zu, in der sie ausgegeben
    werden, aber Wiederholungen können bei Fehlern die Reihenfolge ändern.
    Deduplizieren Sie immer und gleichen Sie anhand von `call_id` /
    Objekt-ID ab.
  </Accordion>

  <Accordion title="Duplikate">
    Die Zustellung erfolgt **mindestens einmal**: Eine Wiederholung nach
    einer Antwort, die wir nie gesehen haben, kann ein Ereignis duplizieren.
    Jede Wiederholung enthält dieselbe `event_id`; speichern Sie daher
    verarbeitete IDs und überspringen Sie Wiederholungen. `event_id` wird
    auch zwischen Endpunkten geteilt — zwei Endpunkte, die dasselbe Ereignis
    abonniert haben, erhalten dieselbe `event_id`.
  </Accordion>

  <Accordion title="Zeitüberschreitungen">
    Endpunkt-Zustellungen haben pro Versuch eine Zeitüberschreitung von
    **30 s**. Im älteren Pfad werden blockierende Anfragen, die das
    Verhalten aktiver Anrufe steuern — der
    Konfigurationsaustausch [`telephony.incoming` / `web.incoming`](/de/webhooks/call-incoming) —
    nach **10 s** abgebrochen. Eine langsame Antwort verzögert jedoch die
    Annahme des Anrufs, daher sollten Sie innerhalb weniger Sekunden
    antworten. [Tool-Dispatch im Webhook-Modus](/de/tools/overview) erlaubt 20 s.
  </Accordion>

  <Accordion title="Quell-IP-Adressen">
    Ausgehende Webhooks stammen aus dem Cloud-IP-Bereich von ThunderPhone.
    Wenn Ihre Firewall eine Zulassungsliste erfordert, kontaktieren Sie den
    Support, und wir teilen Ihnen die aktuellen Bereiche mit.
  </Accordion>
</AccordionGroup>

## Auswahl zwischen älteren und endpunktbasierten Webhooks

| Merkmal                               | Älter (`/v1/webhook`)                                                    | Endpunkte (`/v1/developer/webhook-endpoints`) |
| ------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------- |
| Anzahl der URLs                       | 1 pro Organisation                                                       | Mehrere pro Organisation                      |
| Ereignisabdeckung                     | Nur `telephony.*` / `web.*`                                              | Alle 10 Ereignistypen                         |
| Ereignisfilter                        | —                                                                        | Pro Endpunkt                                  |
| Wiederholungen                        | Keine                                                                    | 8 Versuche über 24 h                          |
| Umschlag                              | `type` + `data`                                                          | `type` + `data` + `event_id`                  |
| Secret-Rotation                       | Ersetzt einzelnes Secret                                                 | Secret pro Endpunkt                           |
| Ohne Löschen deaktivieren             | —                                                                        | `status=disabled`                             |
| Statussichtbarkeit                    | —                                                                        | `active` / `disabled` / `failing`             |
| Blockierender Konfigurationsaustausch | Ja ([`telephony.incoming` / `web.incoming`](/de/webhooks/call-incoming)) | Niemals — nur Benachrichtigungen              |
| Am besten geeignet für                | Dynamische Anrufkonfiguration                                            | Ereignisverarbeitung in der Produktion        |

Neue Integrationen sollten Ereignisse über endpunktbasierte Webhooks
verarbeiten. Behalten Sie eine ältere URL nur bei (oder fügen Sie eine
hinzu), wenn Sie Anrufe bei der Annahme dynamisch konfigurieren oder
Tool-Dispatch im Webhook-Modus verwenden — diese Anfrage-/Antwort-
Austausche laufen nur über den älteren Pfad.

***

## Verwandte Inhalte

<CardGroup cols={2}>
  <Card title="Ereigniskatalog" icon="list" href="/de/webhooks/events">
    Alle Ereignistypen und ihre Nutzlasten.
  </Card>

  <Card title="Webhook-Endpunkte" icon="bolt" href="/de/webhooks/endpoints">
    Verwalten Sie mehrere Endpunkte, Ereignisfilter und Secrets.
  </Card>

  <Card title="telephony.incoming / web.incoming" icon="phone" href="/de/webhooks/call-incoming">
    Die blockierende Anfrage, die Ihr Server zur Konfiguration von Anrufen beantworten muss.
  </Card>

  <Card title="telephony.complete / web.complete" icon="phone" href="/de/webhooks/call-complete">
    Nutzlast nach dem Anruf mit Transkript, Aufzeichnung und Metriken.
  </Card>
</CardGroup>
