ThunderPhone 2.0 ist live.Direkt im Self-Service – ab 2 ¢/Min..Ankündigung lesen

Webhooks

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:

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

  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, 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.

Python
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
Node.js (Express)
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);
  },
);

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 URLs1 pro OrganisationMehrere pro Organisation
EreignisabdeckungNur telephony.* / web.*Alle 10 Ereignistypen
EreignisfilterPro Endpunkt
WiederholungenKeine8 Versuche über 24 h
Umschlagtype + datatype + data + event_id
GeheimnisrotationErsetzt einzelnes GeheimnisGeheimnis pro Endpunkt
Deaktivieren ohne Löschenstatus=disabled
Statussichtbarkeitactive / disabled / failing
Blockierender KonfigurationsaustauschJa (telephony.incoming / web.incoming)Nie — nur Benachrichtigungen
Am besten geeignet fürDynamische AnrufkonfigurationEreignisverarbeitung 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