Oversikt over webhooks
Slik leverer ThunderPhone sanntidshendelser, slik verifiserer du signaturer, og slik sammenlignes de eldre og endepunktbaserte leveringsmodellene.
ThunderPhone sender HTTP-POST-forespørsler til serveren din når noe
skjer under en samtale — et innkommende anrop starter, en samtale avsluttes, en
evalueringskjøring fullføres, et varsel utløses og så videre. Det finnes to
leveringsmodeller:
Flere URL-er, hemmeligheter per endepunkt, hendelsesfiltre per endepunkt
og automatiske nye forsøk.
Administrer via GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Én URL per organisasjon. Inneholder hendelser i samtalens livssyklus, inkludert
de blokkerende konfigurasjonsutvekslingene. Administreres med GET/PUT /v1/webhook.
Alle de ti hendelsestypene i hendelseskatalogen leveres
gjennom webhook-endepunkter. De seks hendelsene i samtalens livssyklus
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) sendes også til den
eldre webhooken med én URL — hvis du har både en eldre URL og et
samsvarende endepunkt, mottar du hendelsen på begge banene. Blokkerende
atferd (konfigurasjonsutvekslingen for telephony.incoming / web.incoming
og verktøyutsending i webhook-modus)
finnes utelukkende på den eldre banen; hver levering til et endepunkt er et
varsel uten venting på svar.
Nyttelastformat
Leveringer til endepunkter er et JSON-objekt med data, event_id og
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id er unik for hver utsendte hendelse. Den er identisk på tvers av nye forsøk
og på tvers av alle endepunkter som mottar hendelsen — bruk den til deduplisering.
Den eldre webhooken med én URL sender samme type og data, men
uten event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}Ved overføring serialiseres hver brødtekst kanonisk — nøkler sorteres alfabetisk, uten mellomrom, UTF-8. De pent formaterte eksemplene i denne dokumentasjonen er kun for lesbarhet.
Se hendelseskatalogen for en fullstendig liste over hendelses- typer og nyttelastfelt.
Signaturverifisering
Hver forespørsel har en HMAC-SHA256-signatur over den rå
forespørselsbrødteksten i X-ThunderPhone-Signature-headeren. Signeringsnøkkelen er
endepunktets secret (eller webhookens secret på organisasjonsnivå for eldre
leveringer).
Trinn
- Les den rå forespørselsbrødteksten før eventuell parsing.
- Beregn
hmac_sha256(secret, body).hexdigest(). - Sammenlign i konstant tid med
X-ThunderPhone-Signature-headeren.
Vi signerer nøyaktig byteene vi overfører, og disse byteene er den kanoniske JSON-serialiseringen (sorterte nøkler, kompakte skilletegn). Derfor fungerer verifisering mot råbrødteksten alltid — og hvis rammeverket ditt bare gir deg parsede JSON-data, produserer reserialisering med sorterte nøkler og kompakte skilletegn identiske byte. Begge fremgangsmåtene er dekket i verifiseringsveiledningen.
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);
},
);Leveringssemantikk
Denne semantikken gjelder leveringer til endepunkt. Den eldre webhooken med én URL er ett enkelt synkront forsøk uten nye forsøk.
Nye forsøk
Hver hendelse forsøkes levert én gang umiddelbart. Ethvert 2xx-svar
bekrefter leveringen. Ved ethvert annet utfall (ikke-2xx,
tilkoblingsfeil, tidsavbrudd) prøver vi på nytt 1 min, 5 min, 30 min, 2 t, 6 t,
12 t og 24 t etter første forsøk — 8 forsøk fordelt over
24 timer. Hvis alle forsøk mislykkes, stopper leveringen, og endepunktet
markeres med status="failing" i
webhook-endepunkter. Returner 2xx så snart
nyttelasten er varig mottatt; behandle den asynkront.
Rekkefølge
Leveringsrekkefølgen er etter beste evne. I praksis leverer vi i
rekkefølgen hendelser sendes ut, men nye forsøk kan endre rekkefølgen ved feil.
Dedupliser alltid og avstem etter call_id / objekt-ID.
Duplikater
Levering er minst én gang: et nytt forsøk etter et svar vi aldri
mottok, kan duplisere en hendelse. Hvert nytt forsøk har samme
event_id, så lagre behandlede ID-er og hopp over gjentakelser. event_id er
også delt på tvers av endepunkter — to endepunkter som abonnerer på den
samme hendelsen, mottar samme event_id.
Tidsavbrudd
Leveringer til endepunkter har et tidsavbrudd på 30 s per forsøk. På den
eldre ruten har blokkerende forespørsler som styrer aktive samtaler —
konfigurasjonsutvekslingen
telephony.incoming / web.incoming —
tidsavbrudd etter 10 s, men et tregt svar forsinker besvarelsen av samtalen,
så prøv å svare innen et par sekunder. Verktøydistribuering i
webhook-modus tillater 20 s som standard, og verktøydeklarasjoner kan angi et
timeout på øverste nivå.
Kilde-IP-adresser
Utgående webhooks kommer fra ThunderPhones IP-område i skyen. Hvis brannmuren din krever en tillatelsesliste, kan du kontakte support, så deler vi de gjeldende områdene.
Velge mellom eldre og endepunktbaserte webhooks
| Funksjon | Eldre (/v1/webhook) | Endepunkter (/v1/developer/webhook-endpoints) |
|---|---|---|
| Antall URL-er | 1 per organisasjon | Mange per organisasjon |
| Hendelsesdekning | Bare telephony.* / web.* | Alle 10 hendelsestyper |
| Hendelsesfilter | — | Per endepunkt |
| Nye forsøk | Ingen | 8 forsøk over 24 t |
| Innpakning | type + data | type + data + event_id |
| Rotering av hemmelig nøkkel | Erstatter én hemmelig nøkkel | Hemmelig nøkkel per endepunkt |
| Deaktiver uten å slette | — | status=disabled |
| Synlighet for status | — | active / disabled / failing |
| Blokkerende konfigurasjonsutveksling | Ja (telephony.incoming / web.incoming) | Aldri — kun varsler |
| Best for | Dynamisk samtalekonfigurasjon | Hendelseshåndtering i produksjon |
Nye integrasjoner bør motta hendelser gjennom endepunktbaserte webhooks. Behold (eller legg til) en eldre URL bare hvis du konfigurerer samtaler dynamisk ved besvarelse eller bruker verktøydistribuering i webhook-modus — disse forespørsels-/svarsutvekslingene kjører bare på den eldre ruten.
Relatert
Alle hendelsestyper og nyttelastene deres.
Administrer flere endepunkter, hendelsesfiltre og hemmelige nøkler.
Den blokkerende forespørselen serveren din må svare på for å konfigurere samtaler.
Nyttelast etter samtalen med transkripsjon, opptak og måledata.