Open in
Webhooks – Übersicht
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:
Mehrere URLs, Geheimnisse pro Endpunkt, Ereignisfilter pro Endpunkt
und automatische Wiederholungen.
Verwaltung über GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Eine URL pro Organisation. Enthält die Ereignisse des Anruflebenszyklus, einschließlich der
blockierenden Konfigurationsaustausche. Verwaltung über GET/PUT /v1/webhook.
Alle aufgeführten 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 Konfigurationsaustausch für telephony.incoming / web.incoming
und die Tool-Weiterleitung 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:
{
"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:
{
"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 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, derentmehr 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-Signaturesigniert nur den Rohtextkörper. Sie wird weiterhin unverändert gesendet, damit vorhandene Verifizierer weiterhin funktionieren.
Schritte
- Lesen Sie den Header, die Methode, den Rohpfad und die Abfrage sowie den rohen Body vor jeder Verarbeitung.
- Berechnen Sie den hexadezimalen HMAC-SHA256 von
<t>\n<METHOD>\n<path and query>\n<body>mit Ihrem Secret. - Lehnen Sie die Anfrage ab, wenn
tum mehr als 300 Sekunden abweicht oder das Ergebnis bei einem Vergleich mit konstanter Laufzeit nicht mitv1ü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 enthält den exakt signierten String, ein durchgerechnetes Beispiel, Code ohne SDK und den veralteten Header.
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 "", 204import 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);
},
);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, der einmal wiederholt wird.
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 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.
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.
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.
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
— 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 im Webhook-Modus 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 Allowlist erfordert, kontaktieren Sie den Support. Wir teilen Ihnen dann die aktuellen Bereiche mit.
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) | 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
Alle Ereignistypen und ihre Nutzdaten.
Verwalten Sie mehrere Endpunkte, Ereignisfilter und Secrets.
Die blockierende Anfrage, die Ihr Server zur Konfiguration von Anrufen beantworten muss.
Nutzdaten nach dem Anruf mit Transkript, Aufzeichnung und Metriken.