Webhooks – Überblick
Wie ThunderPhone Echtzeitereignisse bereitstellt, wie Sie Signaturen verifizieren und wie sich die Legacy- und Endpunkt-basierten 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 und so weiter. Es gibt zwei Zustellmodelle:
Mehrere URLs, Secrets pro Endpunkt, Ereignisfilter pro Endpunkt
und automatische Wiederholungsversuche.
Verwaltung über GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Eine URL pro Organisation. Überträgt die Ereignisse des
Anruflebenszyklus, einschließlich der blockierenden Konfigurationsaustausche.
Verwaltung über GET/PUT /v1/webhook.
Alle zehn Ereignistypen im Ereigniskatalog 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
telephony.incoming- / web.incoming-Konfigurationsaustausch
und der Tool-Versand im Webhook-Modus)
ist ausschließlich im 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:
{
"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 ausgelöste Ereignis eindeutig. Sie bleibt bei
Wiederholungsversuchen und bei jedem Endpunkt, der das Ereignis empfängt,
identisch — entfernen Sie Duplikate anhand dieser ID.
Der Legacy-Webhook mit einzelner URL sendet denselben type und dieselben
data, jedoch ohne event_id:
{
"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 Leerzeichen, UTF-8. Die formatierten Beispiele in dieser Dokumentation dienen ausschließlich der Lesbarkeit.
Im Ereigniskatalog finden Sie die vollständige Liste der Ereignistypen und Payload-Felder.
Signaturverifizierung
Jede Anfrage enthält eine HMAC-SHA256-Signatur über den rohen Anfrage-Body
im Header X-ThunderPhone-Signature. Der Signaturschlüssel ist das
secret des Endpunkts (oder Ihr Webhook-secret auf Organisationsebene für Legacy-Zustellungen).
Schritte
- Lesen Sie den rohen Anfrage-Body vor jeder Verarbeitung.
- Berechnen Sie
hmac_sha256(secret, body).hexdigest(). - Vergleichen Sie das Ergebnis in konstanter Zeit mit dem Header
X-ThunderPhone-Signature.
Wir signieren exakt die Bytes, die wir übertragen, und diese Bytes sind die kanonische JSON-Serialisierung (sortierte Schlüssel, kompakte Trennzeichen). Daher funktioniert die Verifizierung 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 Verifizierung behandelt.
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 "", 204import 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);
},
);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.
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 mit status="failing"
markiert. Geben Sie 2xx zurück, sobald die Nutzdaten dauerhaft
angenommen wurden; verarbeiten Sie sie asynchron.
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. Deduplizieren Sie immer und gleichen Sie anhand von
call_id / Objekt-ID ab.
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 endpunktübergreifend geteilt — zwei Endpunkte,
die dasselbe Ereignis abonniert haben, erhalten dieselbe event_id.
Zeitüberschreitungen
Zustellungen an Endpunkte haben pro Versuch eine Zeitüberschreitung
von 30 s. Im älteren Pfad laufen blockierende Anfragen, die das
Verhalten aktiver Anrufe steuern — der
telephony.incoming / web.incoming-
Konfigurationsaustausch — nach 10 s ab. Eine langsame Antwort
verzögert jedoch die Annahme des Anrufs, daher sollten Sie innerhalb
weniger Sekunden antworten. Die webhookbasierte
Tool-Weiterleitung erlaubt standardmäßig 20 s,
und Tool-Deklarationen können ein timeout auf oberster Ebene
festlegen.
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.
Wahl zwischen älteren und endpunktbasierten Webhooks
| Funktion | Ä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 |
| Geheimnisrotation | Ersetzt einzelnes Geheimnis | Geheimnis pro Endpunkt |
| Deaktivieren ohne Löschen | — | status=disabled |
| Statussichtbarkeit | — | active / disabled / failing |
| Blockierender Konfigurationsaustausch | Ja (telephony.incoming / web.incoming) | Nie — nur Benachrichtigungen |
| Am besten geeignet für | Dynamische Anrufkonfiguration | Ereignisverarbeitung in 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 die webhookbasierte Tool-Weiterleitung verwenden — diese Anfrage-/Antwort- Austausche laufen nur über den älteren Pfad.
Verwandte Inhalte
Alle Ereignistypen und ihre Nutzdaten.
Verwalten Sie mehrere Endpunkte, Ereignisfilter und Geheimnisse.
Die blockierende Anfrage, die Ihr Server zur Konfiguration von Anrufen beantworten muss.
Nutzdaten nach dem Anruf mit Transkript, Aufzeichnung und Metriken.