Open in
Overzicht van webhooks
Hoe ThunderPhone realtime gebeurtenissen levert, hoe je handtekeningen verifieert en hoe de verouderde en eindpuntgebaseerde leveringsmodellen zich tot elkaar verhouden.
ThunderPhone stuurt HTTP-POST-verzoeken naar je server wanneer er iets
gebeurt tijdens een oproep — een inkomende oproep begint, een oproep eindigt, een beoordelingsrun
wordt voltooid, een waarschuwing wordt geactiveerd, enzovoort. Er zijn twee leveringsmodellen:
Meerdere URL's, geheimen per eindpunt, gebeurtenisfilters per eindpunt
en automatische nieuwe pogingen.
Beheer via GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Eén URL per organisatie. Bevat de gebeurtenissen in de
oproeplevenscyclus, inclusief de blokkerende configuratie-uitwisselingen.
Beheer via GET/PUT /v1/webhook.
Alle vermelde gebeurtenistypen in de gebeurteniscatalogus worden
geleverd via webhook-eindpunten. De zes gebeurtenissen in de oproeplevenscyclus
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) worden ook naar de
verouderde webhook met één URL gestuurd — als je zowel een verouderde URL als een
overeenkomend eindpunt hebt, ontvang je de gebeurtenis via beide paden. Blokkerend
gedrag (de configuratie-uitwisseling voor telephony.incoming / web.incoming
en tool-dispatch in webhookmodus)
bestaat uitsluitend op het verouderde pad; elke levering aan een eindpunt is een
fire-and-forgetmelding.
Payloadindeling
Leveringen aan eindpunten zijn een JSON-object met data, event_id en
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id is uniek voor elke verzonden gebeurtenis. Het is identiek bij nieuwe pogingen
en bij elk eindpunt dat de gebeurtenis ontvangt — dedupliceer op basis hiervan.
De verouderde webhook met één URL stuurt hetzelfde type en dezelfde data, maar
zonder event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}Over het netwerk wordt elke body canoniek geserialiseerd — sleutels alfabetisch gesorteerd, geen witruimte, UTF-8. De mooi opgemaakte voorbeelden in deze documentatie zijn uitsluitend bedoeld voor leesbaarheid.
Zie de gebeurteniscatalogus voor de volledige lijst met gebeurtenistypen en payloadvelden.
Handtekeningverificatie
Elke aanvraag bevat twee handtekeningen, beide HMAC-SHA256 met als sleutel de
secret van het eindpunt (of je webhook-secret op organisatieniveau voor verouderde
leveringen):
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>ondertekent de tijdstempel, de methode, het pad en de query, en de onbewerkte body. Verifieer deze en weiger aanvragen waarvantmeer dan 5 minuten afwijkt van je klok, zodat een onderschepte aanvraag later niet opnieuw kan worden afgespeeld. Nieuwe pogingen worden opnieuw ondertekend wanneer ze worden verzonden.X-ThunderPhone-Signatureondertekent alleen de onbewerkte body. Deze wordt nog steeds ongewijzigd verzonden, zodat bestaande verificateurs blijven werken.
Stappen
- Lees de header, de methode, het onbewerkte pad en de query, en de onbewerkte body vóór enige parsing.
- Bereken de hexadecimale HMAC-SHA256 van
<t>\n<METHOD>\n<path and query>\n<body>met je geheim. - Weiger de aanvraag als
tmeer dan 300 seconden afwijkt, of als het resultaat niet overeenkomt metv1bij een vergelijking in constante tijd.
Bodies zijn de canonieke JSON-serialisatie (gesorteerde sleutels, compacte scheidingstekens), en we ondertekenen exact de bytes die we verzenden. De verificatiehandleiding bevat de exacte ondertekende tekenreeks, een uitgewerkt voorbeeld, code zonder de SDK en de verouderde 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);
},
);Bezorgsemantiek
Deze semantiek geldt voor leveringen aan endpoints. De verouderde
webhook met één URL voert één synchrone poging uit zonder herpogingen, behalve de
configuratie-uitwisseling voor telefoongesprekken van
telephony.incoming,
waarvoor één herpoging wordt uitgevoerd.
Herpogingen
Elk event wordt onmiddellijk één keer geprobeerd. Elke 2xx-respons
bevestigt de levering. Bij elke andere uitkomst (geen 2xx,
verbindingsfout, time-out) proberen we het opnieuw na 1 m, 5 m, 30 m, 2 h, 6 h,
12 h en 24 h na de eerste poging — 8 pogingen verspreid over
24 uur. Als elke poging mislukt, stopt de levering en wordt het endpoint
gemarkeerd met status="failing" in
webhook-endpoints; we e-mailen de
eigenaren en beheerders van je organisatie en tonen dit in het dashboard. Retourneer 2xx zodra
de payload duurzaam is geaccepteerd; verwerk deze asynchroon.
Testverzendingen vanuit de endpoint-test-API krijgen ongeveer een minuut na
de eerste poging één herpoging en wijzigen de status van het endpoint nooit.
Volgorde
De bezorgvolgorde is naar beste vermogen. In de praktijk leveren we in de
volgorde waarin gebeurtenissen worden verzonden, maar nieuwe pogingen kunnen bij fouten
de volgorde wijzigen.
Dedupliqueer en stem altijd af op basis van call_id / object-id.
Duplicaten
Bezorging is minstens één keer: een nieuwe poging na een reactie die we nooit
hebben gezien, kan een gebeurtenis dupliceren. Elke nieuwe poging bevat dezelfde
event_id, dus sla verwerkte id's op en sla herhalingen over. event_id wordt
ook gedeeld tussen eindpunten — twee eindpunten die op dezelfde
gebeurtenis zijn geabonneerd ontvangen dezelfde event_id.
Time-outs
Leveringen aan endpoints hebben een time-out van 30 s per poging. Op het
verouderde pad zijn blokkerende aanvragen die het gedrag van live oproepen aansturen — de
configuratie-uitwisseling van
telephony.incoming / web.incoming
— beperkt tot 10 s voor de eerste poging van een telefoongesprek en
13 s voor de volledige uitwisseling, inclusief de ene
herpoging, en tot 10 s voor een widgetsessie. Een trage respons vertraagt
het opnemen van een oproep, dus probeer binnen enkele seconden te antwoorden. Toondispatch in webhookmodus tooldispatch staat standaard 20 s
toe, en tooldeclaraties kunnen een timeout op hoofdniveau instellen.
Bron-IP's
Uitgaande webhooks zijn afkomstig uit het cloud-IP-bereik van ThunderPhone. Als je firewall een toelatingslijst vereist, neem dan contact op met support; we delen de actuele bereiken.
Kiezen tussen verouderde en op eindpunten gebaseerde webhooks
| Functie | Legacy (/v1/webhook) | Eindpunten (/v1/developer/webhook-endpoints) |
|---|---|---|
| Aantal URL's | 1 per organisatie | Meerdere per organisatie |
| Gebeurtenisdekking | Alleen telephony.* / web.* | Alle 10 gebeurtenistypen |
| Gebeurtenisfilter | — | Per eindpunt |
| Nieuwe pogingen | Geen, behalve één voor de configuratie-uitwisseling voor telefoongesprekken | 8 pogingen binnen 24 uur |
| Envelop | type + data | type + data + event_id |
| Secretrotatie | Vervangt één secret | Secret per eindpunt |
| Uitschakelen zonder verwijderen | PUT /v1/webhook met {"url": ""} | status=disabled |
| Zichtbaarheid van status | — | active / disabled / failing |
| Blokkerende configuratie-uitwisseling | Ja (telephony.incoming / web.incoming) | Nooit — alleen meldingen |
| Meest geschikt voor | Dynamische oproepconfiguratie | Gebeurtenissen verwerken in productie |
Nieuwe integraties moeten gebeurtenissen verwerken via op eindpunten gebaseerde webhooks. Behoud (of voeg) alleen een verouderde URL toe als je oproepen dynamisch configureert bij het aannemen of toolverzending in webhookmodus gebruikt — die verzoek-/reactie-uitwisselingen worden alleen uitgevoerd via het verouderde pad.
Gerelateerd
Alle gebeurtenistypen en hun payloads.
Beheer meerdere eindpunten, gebeurtenisfilters en geheimen.
Het blokkerende verzoek waarop je server moet antwoorden om oproepen te configureren.
Payload na het gesprek met transcript, opname en statistieken.