Přehled webhooků
Jak ThunderPhone doručuje události v reálném čase, jak ověřovat podpisy a jak se porovnávají starší model doručování a model založený na koncových bodech.
ThunderPhone odesílá na váš server požadavky HTTP POST, když během hovoru
nastanou události — začne příchozí hovor, hovor skončí, dokončí se
spuštění hodnocení, aktivuje se upozornění a podobně. Existují dva modely
doručování:
Více adres URL, tajné klíče pro jednotlivé endpointy, filtry událostí pro jednotlivé endpointy
a automatické opakování pokusů.
Spravujte pomocí GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Jedna adresa URL pro každou organizaci. Obsahuje události životního cyklu hovoru včetně
blokujících výměn konfigurace. Spravuje se pomocí GET/PUT /v1/webhook.
Všech deset typů událostí v katalogu událostí je
doručováno prostřednictvím webhookových endpointů. Šest událostí životního cyklu hovoru
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) je také odesíláno do
staršího webhooku s jednou adresou URL — pokud máte starší adresu URL i
odpovídající endpoint, obdržíte událost na obou cestách. Blokující
chování (výměna konfigurace telephony.incoming / web.incoming
a distribuce nástrojů v režimu
webhooku) funguje výhradně na starší cestě; každé doručení na endpoint je
oznámení bez čekání na odpověď.
Formát datové části
Doručení na endpoint obsahují 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é pro každou odeslanou událost. Je stejné při opakovaných pokusech
i na každém endpointu, který událost obdrží — používejte je pro deduplikaci.
Starší webhook s jednou adresou URL odesílá stejné položky type a data, ale
bez event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}Při přenosu je každá datová část serializována kanonicky — klíče jsou řazeny abecedně, bez mezer, v UTF-8. Příklady s formátováním v této dokumentaci slouží pouze pro lepší čitelnost.
Úplný seznam typů událostí a polí datové části najdete v katalogu událostí.
Ověření podpisu
Každý požadavek obsahuje podpis HMAC-SHA256 nad nezpracovaným tělem
požadavku v hlavičce X-ThunderPhone-Signature. Podepisovací klíč je
secret koncového bodu (nebo secret webhooku na úrovni vaší organizace pro starší
doručování).
Postup
- Přečtěte nezpracované tělo požadavku před jakýmkoli parsováním.
- Vypočítejte
hmac_sha256(secret, body).hexdigest(). - Porovnejte jej v konstantním čase s hlavičkou
X-ThunderPhone-Signature.
Podepisujeme přesně ty bajty, které odesíláme, a tyto bajty představují kanonickou serializaci JSON (seřazené klíče, kompaktní oddělovače). Ověření oproti nezpracovanému tělu proto vždy funguje — a pokud vám framework předá pouze parsovaný JSON, jeho opětovná serializace se seřazenými klíči a kompaktními oddělovači vytvoří totožné bajty. Oba postupy jsou popsány v průvodci ověření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 "", 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);
},
);Sémantika doručování
Tato sémantika se vztahuje na doručování do koncových bodů. Starší webhook s jednou adresou URL představuje jediný synchronní pokus bez opakování.
Opakování
Každá událost je jednou okamžitě odeslána. Jakákoli odpověď 2xx
potvrzuje doručení. Při jakémkoli jiném výsledku (jiném než 2xx,
chybě připojení, vypršení časového limitu) opakujeme pokus 1 min, 5 min, 30 min, 2 hod, 6 hod,
12 hod a 24 hod po prvním pokusu — celkem 8 pokusů během
24 hodin. Pokud selžou všechny pokusy, doručování se zastaví a koncový bod
je v koncových bodech webhooků označen jako
status="failing". Vraťte 2xx, jakmile je datová část trvale přijata;
zpracujte ji asynchronně.
Pořadí
Pořadí doručování je založeno na principu best effort. V praxi doručujeme události
v pořadí, v jakém jsou emitovány, ale při selhání je opakování může změnit.
Vždy deduplikujte a slaďte podle call_id / ID objektu.
Duplikáty
Doručování je alespoň jednou: opakování po odpovědi, kterou jsme
neobdrželi, může událost duplikovat. Každé opakování obsahuje stejné
event_id, proto ukládejte ID zpracovaných událostí a opakování přeskočte. event_id je
také sdíleno mezi koncovými body — dva koncové body přihlášené ke stejné
události obdrží stejné event_id.
Časové limity
Doručování do koncových bodů má časový limit 30 s na každý pokus. Ve
starší cestě blokující požadavky, které řídí chování živého hovoru — výměna
konfigurace telephony.incoming / web.incoming —
vyprší po 10 s, ale pomalá odpověď zpozdí přijetí hovoru, proto se
snažte odpovědět během několika sekund. Spouštění nástrojů v režimu webhooků
ve výchozím nastavení umožňuje 20 s a deklarace nástrojů mohou nastavit
timeout na nejvyšší úrovni.
Zdrojové IP adresy
Odchozí webhooky pocházejí z cloudového rozsahu IP adres ThunderPhone. Pokud váš firewall vyžaduje seznam povolených adres, kontaktujte podporu a sdělíme vám aktuální rozsahy.
Volba mezi staršími webhooky a webhooky koncových bodů
| Funkce | Starší (/v1/webhook) | Koncové body (/v1/developer/webhook-endpoints) |
|---|---|---|
| Počet adres URL | 1 na organizaci | Více na organizaci |
| Pokrytí událostí | Pouze telephony.* / web.* | Všech 10 typů událostí |
| Filtr událostí | — | Pro každý koncový bod |
| Opakování | Žádné | 8 pokusů během 24 hod |
| Obálka | type + data | type + data + event_id |
| Rotace tajného klíče | Nahrazuje jediný tajný klíč | Tajný klíč pro každý koncový bod |
| Zakázání bez odstranění | — | status=disabled |
| Viditelnost stavu | — | active / disabled / failing |
| Blokující výměna konfigurace | Ano (telephony.incoming / web.incoming) | Nikdy — pouze oznámení |
| Nejvhodnější pro | Dynamickou konfiguraci hovoru | Zpracování událostí v produkci |
Nové integrace by měly události zpracovávat prostřednictvím webhooků koncových bodů. Starší adresu URL ponechte (nebo přidejte) pouze tehdy, pokud hovory dynamicky konfigurujete při jejich přijetí nebo používáte spouštění nástrojů v režimu webhooků — tyto výměny požadavků a odpovědí fungují pouze ve starší cestě.
Související
Všechny typy událostí a jejich datové části.
Spravujte více koncových bodů, filtry událostí a tajné klíče.
Blokující požadavek, na který musí váš server odpovědět, aby nakonfiguroval hovory.
Datová část po hovoru s přepisem, nahrávkou a metrikami.