---
title: "Webhooks – Übersicht"
description: "Wie ThunderPhone Ereignisse in Echtzeit übermittelt, wie Sie Signaturen verifizieren und wie sich die Legacy- und die endpunktbasierten Zustellmodelle vergleichen."
---

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

<CardGroup cols={2}>
  <Card title="Webhook-Endpunkte (empfohlen)" icon="bolt" href="/de/webhooks/endpoints">
    Mehrere URLs, Geheimnisse pro Endpunkt, Ereignisfilter pro Endpunkt
    und automatische Wiederholungen.
    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 aufgeführten 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 **auch** 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 [Konfigurationsaustausch für `telephony.incoming` / `web.incoming`](/de/webhooks/call-incoming)
und die [Tool-Weiterleitung](/de/tools/overview) im Webhook-Modus)
ist ausschließlich auf dem Legacy-Pfad verfügbar; jede Endpunktzustellung ist eine
Fire-and-Forget-Benachrichtigung.

## Payload-Format

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

```json
{
  "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 Wiederholungen
**und** bei jedem Endpunkt, 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
{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
```

Bei der Übertragung wird jeder Body kanonisch serialisiert — Schlüssel werden alphabetisch
sortiert, ohne Leerzeichen, in 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.

## Signaturverifizierung

Jede Anfrage enthält zwei Signaturen, die beide mit HMAC-SHA256 und dem
`secret` des Endpunkts signiert sind (oder dem Webhook-`secret` Ihrer Organisation für Legacy-Zustellungen):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` signiert den
  Zeitstempel, die Methode, den Pfad und die Abfrage sowie den Rohtextkörper. Überprüfen Sie
  diese Signatur und lehnen Sie Anfragen ab, deren `t` mehr als **5 Minuten** von
  Ihrer Uhr abweicht, damit eine abgefangene Anfrage später nicht erneut abgespielt werden kann. Wiederholungsversuche werden beim Senden erneut
  signiert.
- `X-ThunderPhone-Signature` signiert nur den Rohtextkörper. Sie wird weiterhin
  unverändert gesendet, damit vorhandene Verifizierer weiterhin funktionieren.

### Schritte

1. Lesen Sie den Header, die Methode, den Rohpfad und die Abfrage sowie den rohen
   Body **vor** jeder Verarbeitung.
2. Berechnen Sie den hexadezimalen HMAC-SHA256 von
   `<t>\n<METHOD>\n<path and query>\n<body>` mit Ihrem Secret.
3. Lehnen Sie die Anfrage ab, wenn `t` um mehr als 300 Sekunden abweicht oder das
   Ergebnis bei einem Vergleich mit konstanter Laufzeit nicht mit `v1` übereinstimmt.

Bodys verwenden die kanonische JSON-Serialisierung (sortierte Schlüssel, kompakte
Trennzeichen), und wir signieren genau die Bytes, die wir übertragen. Der
[Leitfaden zur Verifizierung](/de/guides/verify-webhook-signatures) enthält den exakt
signierten String, ein durchgerechnetes Beispiel, Code ohne SDK und den
veralteten Header.

<CodeGroup>
```python Python (Flask)
from flask import Flask, abort, request
from thunderphone import verify_signature

app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    query = request.query_string.decode()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=request.path + (f"?{query}" if query else ""),
        body=request.get_data(),  # raw bytes
    ):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
```

```javascript Node.js (Express)
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl,
      body: req.body,
    });
    if (!ok) 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 einzigen synchronen Versuch ohne Wiederholungen aus, mit Ausnahme des Konfigurationsaustauschs für Telefonanrufe von [`telephony.incoming`](/de/webhooks/call-incoming), der einmal wiederholt wird.

<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 Min., 5 Min., 30 Min., 2 Std., 6 Std.,
    12 Std. und 24 Std. 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; wir senden den Eigentümern und Administratoren Ihrer Organisation eine E-Mail
    und zeigen dies im Dashboard an. Geben Sie `2xx` zurück, sobald
    die Nutzlast dauerhaft angenommen wurde; verarbeiten Sie sie asynchron.
    Testsendungen über die Endpunkt-Test-API erhalten etwa eine Minute nach
    dem ersten Versuch eine Wiederholung und ändern niemals den Status des Endpunkts.
  </Accordion>

  <Accordion title="Reihenfolge">
    Die Zustellreihenfolge erfolgt nach bestem Bemühen. In der Praxis stellen wir Ereignisse in der
    Reihenfolge zu, in der sie ausgegeben werden, aber Wiederholungen können die Reihenfolge bei Fehlern ändern.
    Führen Sie immer Deduplizierung und Abgleich anhand von `call_id` / Objekt-ID durch.
  </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 Endpoints geteilt — zwei Endpoints, die dasselbe Ereignis abonniert haben, erhalten dieselbe `event_id`.
  </Accordion>

  <Accordion title="Zeitüberschreitungen">
    Zustellungen an Endpunkte haben pro Versuch eine Zeitüberschreitung von **30 s**. Im
    älteren Pfad sind blockierende Anfragen, die das Verhalten laufender Anrufe steuern — der
    Konfigurationsaustausch [`telephony.incoming` / `web.incoming`](/de/webhooks/call-incoming)
    — für den ersten Versuch eines Telefonanrufs auf **10 s** und für den gesamten Austausch einschließlich seiner einen
    Wiederholung auf **13 s** begrenzt sowie auf **10 s** für eine Widget-Sitzung. Eine langsame Antwort verzögert
    die Gesprächsannahme; antworten Sie daher innerhalb weniger Sekunden. Der [Tool-Dispatch](/de/tools/overview) im Webhook-Modus erlaubt standardmäßig 20 s,
    und Tool-Deklarationen können ein `timeout` auf oberster Ebene festlegen.
  </Accordion>

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

## Auswahl zwischen älteren und endpointbasierten Webhooks

| Funktion | Legacy (`/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 |
| Wiederholungsversuche | Keine, außer einem für den Austausch zur Anrufkonfiguration | 8 Versuche innerhalb von 24 h |
| Ereignishülle | `type` + `data` | `type` + `data` + `event_id` |
| Secret-Rotation | Ersetzt einzelnes Secret | Secret pro Endpunkt |
| Deaktivieren ohne Löschen | `PUT /v1/webhook` mit `{"url": ""}` | `status=disabled` |
| Statussichtbarkeit | — | `active` / `disabled` / `failing` |
| Blockierender Konfigurationsaustausch | Ja ([`telephony.incoming` / `web.incoming`](/de/webhooks/call-incoming)) | Nie — nur Benachrichtigungen |
| Am besten geeignet für | Dynamische Anrufkonfiguration | Ereignisverarbeitung in Produktion |

Neue Integrationen sollten Ereignisse über endpointbasierte
Webhooks verarbeiten. Behalten Sie eine ältere URL nur bei (oder fügen Sie eine hinzu), wenn Sie Anrufe
beim Zeitpunkt der Annahme dynamisch konfigurieren oder die webhookbasierte Tool-Ausführung verwenden — diese
Anfrage-/Antwortaustausche laufen nur über den älteren Pfad.

---

## Verwandte Inhalte

<CardGroup cols={2}>
  <Card title="Ereigniskatalog" icon="list" href="/de/webhooks/events">
    Alle Ereignistypen und ihre Nutzdaten.
  </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">
    Nutzdaten nach dem Anruf mit Transkript, Aufzeichnung und Metriken.
  </Card>
</CardGroup>
