Prehľad webhookov
ThunderPhone odosiela na váš server požiadavky HTTP POST, keď počas hovoru
nastanú udalosti — začne sa prichádzajúci hovor, hovor sa skončí, dokončí sa
spustenie hodnotenia, aktivuje sa upozornenie a podobne. Existujú dva modely
doručovania:
Viaceré adresy URL, tajné kľúče pre jednotlivé koncové body, filtre udalostí
pre jednotlivé koncové body a automatické opakované pokusy.
Spravujte prostredníctvom GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Jedna adresa URL na organizáciu. Obsahuje udalosti životného cyklu hovoru vrátane
blokujúcich výmen konfigurácie. Spravuje sa prostredníctvom GET/PUT /v1/webhook.
Všetkých desať typov udalostí v katalógu udalostí sa
doručuje prostredníctvom koncových bodov webhookov. Šesť udalostí životného cyklu
hovoru (telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) sa tiež odosiela do
staršieho webhooku s jednou adresou URL — ak máte staršiu adresu URL aj
zodpovedajúci koncový bod, udalosť dostanete na oboch cestách. Blokujúce
správanie (výmena konfigurácie telephony.incoming / web.incoming
a odosielanie nástrojov v režime webhooku)
je výhradne na staršej ceste; každé doručenie do koncového bodu je notifikácia
typu fire-and-forget.
Formát dát
Doručenia do koncových bodov sú objekt JSON s položkami data, event_id a
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}
event_id je jedinečné pre každú vyslanú udalosť. Je rovnaké pri opakovaných
pokusoch aj vo všetkých koncových bodoch, ktoré udalosť prijmú — použite ho
na deduplikáciu.
Starší webhook s jednou adresou URL odosiela rovnaké type a data, ale
bez event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
Pri prenose je každé telo serializované kanonicky — kľúče sú zoradené abecedne, bez medzier, v kódovaní UTF-8. Príklady s formátovaním v tejto dokumentácii slúžia len na lepšiu čitateľnosť.
Úplný zoznam typov udalostí a polí dát nájdete v Katalógu udalostí.
Overenie podpisu
Každá požiadavka obsahuje podpis HMAC-SHA256 nad nespracovaným telom
požiadavky v hlavičke X-ThunderPhone-Signature. Podpisovací kľúč je
secret koncového bodu (alebo váš secret webhooku na úrovni organizácie
pre staršie doručenia).
Kroky
- Prečítajte nespracované telo požiadavky pred akýmkoľvek parsovaním.
- Vypočítajte
hmac_sha256(secret, body).hexdigest(). - Porovnajte ho v konštantnom čase s hlavičkou
X-ThunderPhone-Signature.
Podpisujeme presne tie bajty, ktoré odosielame, a tieto bajty predstavujú kanonickú serializáciu JSON (zoradené kľúče, kompaktné oddeľovače). Overenie voči nespracovanému telu preto vždy funguje — a ak vám váš framework poskytne iba parsovaný JSON, jeho opätovná serializácia so zoradenými kľúčmi a kompaktnými oddeľovačmi vytvorí identické bajty. Oba postupy sú uvedené v sprievodcovi overením.
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
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);
},
);
Sémantika doručovania
Táto sémantika platí pre doručovanie do koncových bodov. Starší webhook s jednou URL predstavuje jeden synchrónny pokus bez opakovaní.
Opakovania
Každá udalosť sa okamžite skúsi doručiť raz. Akákoľvek odpoveď 2xx
potvrdzuje doručenie. Pri akomkoľvek inom výsledku (iná než 2xx odpoveď,
chyba pripojenia, časový limit) opakujeme pokus 1 min, 5 min, 30 min, 2 h, 6 h,
12 h a 24 h po prvom pokuse — 8 pokusov v priebehu
24 hodín. Ak zlyhajú všetky pokusy, doručovanie sa zastaví a koncový bod
sa v koncových bodoch webhookov označí ako
status="failing". Odpoveď 2xx vráťte hneď po trvalom prijatí
obsahu; spracovanie vykonajte asynchrónne.
Poradie
Poradie doručovania sa vykonáva podľa najlepšieho úsilia. V praxi doručujeme
udalosti v poradí, v akom sú odoslané, opakovania však môžu pri zlyhaní
poradie zmeniť. Vždy odstraňujte duplicity a zosúlaďte údaje podľa call_id /
ID objektu.
Duplikáty
Doručovanie je aspoň raz: opakovanie po odpovedi, ktorú sme
nezaznamenali, môže udalosť duplikovať. Každé opakovanie obsahuje rovnaké
event_id, preto ukladajte spracované ID a opakovania preskakujte. event_id je
zdieľané aj medzi koncovými bodmi — dva koncové body prihlásené na odber
rovnakej udalosti dostanú rovnaké event_id.
Časové limity
Doručovanie do koncových bodov má časový limit 30 s na pokus. Na
staršej ceste majú blokujúce požiadavky, ktoré riadia správanie živého hovoru —
výmena konfigurácie telephony.incoming / web.incoming —
časový limit 10 s, pomalá odpoveď však oneskorí prijatie hovoru, preto
sa snažte odpovedať do niekoľkých sekúnd. Odosielanie nástrojov v režime webhookov
tool dispatch umožňuje 20 s.
Zdrojové IP adresy
Odchádzajúce webhooky pochádzajú z rozsahu cloudových IP adries ThunderPhone. Ak vaša brána firewall vyžaduje zoznam povolených adries, kontaktujte podporu a poskytneme vám aktuálne rozsahy.
Výber medzi staršími webhookmi a webhookmi založenými na koncových bodoch
| Funkcia | Staršie (/v1/webhook) | Koncové body (/v1/developer/webhook-endpoints) |
|---|---|---|
| Počet URL | 1 na organizáciu | Viac na organizáciu |
| Pokrytie udalostí | Iba telephony.* / web.* | Všetkých 10 typov udalostí |
| Filter udalostí | — | Pre každý koncový bod |
| Opakovania | Žiadne | 8 pokusov počas 24 h |
| Obálka | type + data | type + data + event_id |
| Rotácia tajného kľúča | Nahrádza jeden tajný kľúč | Tajný kľúč pre každý koncový bod |
| Zakázanie bez odstránenia | — | status=disabled |
| Viditeľnosť stavu | — | active / disabled / failing |
| Blokujúca výmena konfigurácie | Áno (telephony.incoming / web.incoming) | Nikdy — iba notifikácie |
| Najvhodnejšie na | Dynamickú konfiguráciu hovorov | Spracovanie udalostí v produkcii |
Nové integrácie by mali prijímať udalosti prostredníctvom webhookov založených na koncových bodoch. Staršiu URL ponechajte (alebo pridajte) iba v prípade, že hovory konfigurujete dynamicky pri prijatí hovoru alebo používate odosielanie nástrojov v režime webhookov — tieto výmeny požiadaviek a odpovedí fungujú iba na staršej ceste.
Súvisiace
Všetky typy udalostí a ich obsahy.
Spravujte viacero koncových bodov, filtre udalostí a tajné kľúče.
Blokujúca požiadavka, na ktorú musí váš server odpovedať, aby nakonfiguroval hovory.
Dáta po hovore s prepisom, nahrávkou a metrikami.