Open in
Översikt över webhooks
Hur ThunderPhone levererar händelser i realtid, hur du verifierar signaturer och hur de äldre och endpointbaserade leveransmodellerna jämförs.
ThunderPhone skickar HTTP-POST-begäranden till din server när saker
händer under ett samtal — ett inkommande samtal startar, ett samtal avslutas, en utvärderingskörning slutförs, en avisering utlöses och så vidare. Det finns två leveransmodeller:
Flera URL:er, hemligheter per slutpunkt, händelsefilter per slutpunkt
och automatiska återförsök.
Hantera via GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
En URL per organisation. Innehåller händelser för samtalets livscykel, inklusive
de blockerande konfigurationsutbytena. Hanteras via GET/PUT /v1/webhook.
Alla listade händelsetyper i händelsekatalogen
levereras via webhook-slutpunkter. De sex händelserna för samtalets livscykel
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) skickas också till den
äldre webhooken med en URL — om du har både en äldre URL och en
matchande slutpunkt får du händelsen på båda sökvägarna. Blockerande
beteende (konfigurationsutbytet för
telephony.incoming / web.incoming och
verktygsdirigering i webhook-läge)
finns endast på den äldre sökvägen; varje leverans till en slutpunkt är en
skicka-och-glöm-avisering.
Payloadformat
Leveranser till slutpunkter är ett JSON-objekt med data, event_id och
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id är unikt för varje skickad händelse. Det är identiskt vid återförsök
och för varje slutpunkt som tar emot händelsen — deduplicera utifrån det.
Den äldre webhooken med en URL skickar samma type och data, men
utan event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}Vid överföring serialiseras varje brödtext kanoniskt — nycklar sorteras alfabetiskt, utan blanksteg, UTF-8. De formaterade exemplen i den här dokumentationen är endast för läsbarhet.
Se händelsekatalogen för den fullständiga listan över händelsetyper och payloadfält.
Signaturverifiering
Varje begäran har två signaturer, båda HMAC-SHA256 med endpointens
secret som nyckel (eller din organisations webhook-secret för äldre
leveranser):
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>signerar tidsstämpeln, metoden, sökvägen och frågesträngen samt den råa brödtexten. Verifiera denna och avvisa begäranden varstskiljer sig med mer än 5 minuter från din klocka, så att en uppfångad begäran inte kan spelas upp igen senare. Omförsök signeras på nytt när de skickas.X-ThunderPhone-Signaturesignerar endast den råa brödtexten. Den skickas fortfarande oförändrad så att befintliga verifierare fortsätter att fungera.
Steg
- Läs headern, metoden, den råa sökvägen och frågesträngen samt den råa brödtexten före all tolkning.
- Beräkna hex-HMAC-SHA256 för
<t>\n<METHOD>\n<path and query>\n<body>med din hemlighet. - Avvisa begäran om
tavviker med mer än 300 sekunder eller om resultatet inte matcharv1i en jämförelse med konstant tid.
Brödtexter är den kanoniska JSON-serialiseringen (sorterade nycklar, kompakta avgränsare), och vi signerar exakt de byte vi överför. Guiden för verifiering innehåller den exakta signerade strängen, ett genomarbetat exempel, kod utan SDK:t och den äldre headern.
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);
},
);Leveranssemantik
Dessa semantiker gäller leveranser till slutpunkter. Den äldre webhooken med en enda URL
är ett enda synkront försök utan återförsök, förutom
konfigurationsutbytet för telefonsamtal
telephony.incoming,
som försöks igen en gång.
Återförsök
Varje händelse försöks levereras en gång omedelbart. Alla 2xx-svar
bekräftar leveransen. Vid alla andra utfall (icke-2xx,
anslutningsfel, tidsgräns) försöker vi igen efter 1 m, 5 m, 30 m, 2 h, 6 h,
12 h och 24 h från det första försöket — 8 försök under
24 timmar. Om alla försök misslyckas stoppas leveransen och slutpunkten
markeras med status="failing" i
webhookslutpunkter; vi skickar e-post till organisationens
ägare och administratörer och visar det i instrumentpanelen. Returnera 2xx så snart
nyttolasten har accepterats varaktigt; bearbeta den asynkront.
Testsändningar från slutpunktens test-API får ett återförsök ungefär en minut efter
det första försöket och ändrar aldrig slutpunktens status.
Ordning
Leveransordningen sker efter bästa förmåga. I praktiken levererar vi i den
ordning händelser skapas, men återförsök kan ändra ordningen vid fel.
Avduplicera och stäm alltid av med call_id / objekt-id.
Dubbletter
Leverans sker minst en gång: ett återförsök efter ett svar som vi aldrig
såg kan duplicera en händelse. Varje återförsök har samma
event_id, så lagra bearbetade id:n och hoppa över upprepningar. event_id delas
även mellan ändpunkter — två ändpunkter som prenumererar på samma
händelse får samma event_id.
Tidsgränser
Leveranser till slutpunkter har en tidsgräns på 30 s per försök. På den
äldre sökvägen är blockerande förfrågningar som styr beteendet under pågående samtal —
konfigurationsutbytet
telephony.incoming / web.incoming —
begränsade till 10 s för ett telefonsamtals första försök och 13 s för
hela utbytet inklusive dess enda återförsök, samt till 10 s för en widgetsessions. Ett långsamt svar fördröjer
besvarandet av samtalet, så försök svara inom ett par sekunder. Verktygsanrop i webhookläge tillåter 20 s
som standard, och verktygsdeklarationer kan ange en timeout på toppnivå.
Käll-IP-adresser
Utgående webhooks kommer från ThunderPhones IP-intervall i molnet. Om din brandvägg kräver en tillåtelselista kontaktar du supporten så delar vi de aktuella intervallen.
Välja mellan äldre och ändpunktsbaserade webhooks
| Funktion | Äldre (/v1/webhook) | Slutpunkter (/v1/developer/webhook-endpoints) |
|---|---|---|
| Antal URL:er | 1 per organisation | Flera per organisation |
| Händelsetäckning | Endast telephony.* / web.* | Alla 10 händelsetyper |
| Händelsefilter | — | Per slutpunkt |
| Återförsök | Inga, förutom ett för konfigurationsutbytet för telefonsamtal | 8 försök under 24 h |
| Omslag | type + data | type + data + event_id |
| Rotering av hemlighet | Ersätter en enda hemlighet | Hemlighet per slutpunkt |
| Inaktivera utan att ta bort | PUT /v1/webhook med {"url": ""} | status=disabled |
| Statussynlighet | — | active / disabled / failing |
| Blockerande konfigurationsutbyte | Ja (telephony.incoming / web.incoming) | Aldrig — endast notifieringar |
| Bäst för | Dynamisk samtalskonfiguration | Händelsehantering i produktion |
Nya integrationer bör hantera händelser via ändpunktsbaserade webhooks. Behåll (eller lägg till) en äldre URL endast om du konfigurerar samtal dynamiskt när de besvaras eller använder verktygsanrop i webhookläge — dessa begäran/svar-utbyten körs endast på den äldre sökvägen.
Relaterat
Alla händelsetyper och deras nyttolaster.
Hantera flera slutpunkter, händelsefilter och hemligheter.
Den blockerande begäran som din server måste besvara för att konfigurera samtal.
Nyttolast efter samtalet med transkription, inspelning och mätvärden.