ThunderPhone 2.0 je tady.Začnete bez obchodníka, od 2 ¢/min.Přečíst oznámení

Webhooks

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š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

  1. Přečtěte nezpracované tělo požadavku před jakýmkoli parsováním.
  2. Vypočítejte hmac_sha256(secret, body).hexdigest().
  3. 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.

Python
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
Node.js (Express)
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č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ů

FunkceStarší (/v1/webhook)Koncové body (/v1/developer/webhook-endpoints)
Počet adres URL1 na organizaciVí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álkatype + datatype + data + event_id
Rotace tajného klíčeNahrazuje jediný tajný klíčTajný klíč pro každý koncový bod
Zakázání bez odstraněnístatus=disabled
Viditelnost stavuactive / disabled / failing
Blokující výměna konfiguraceAno (telephony.incoming / web.incoming)Nikdy — pouze oznámení
Nejvhodnější proDynamickou konfiguraci hovoruZpracová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í