ThunderPhone 2.0 je tady.Spustíte sami, 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 vyhodnocení, aktivuje se upozornění a podobně. Existují dva modely doručování:

Všechny uvedené typy událostí v katalogu událostí se doručují prostřednictvím koncových bodů webhooků. Šest událostí životního cyklu hovoru (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) se také odesílá do staršího webhooku s jednou URL — pokud máte starší URL i odpovídající koncový bod, obdržíte událost na obou cestách. Blokující chování (výměna konfigurace telephony.incoming / web.incoming a odesílání nástrojů v režimu webhooku) je dostupné výhradně na starší cestě; každé doručení do koncového bodu je oznámení typu fire-and-forget.

Formát datové části

Doručení do koncového bodu jsou objektem JSON s 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 totožné při opakovaných pokusech i ve všech koncových bodech, které událost obdrží — deduplikujte podle něj.

Starší webhook s jednou URL odesílá stejné 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 kódování 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 naleznete v katalogu událostí.

Ověření podpisu

Každý požadavek obsahuje dva podpisy, oba HMAC-SHA256 s klíčem secret koncového bodu (nebo secret webhooku na úrovni vaší organizace pro starší doručování):

  • X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex> podepisuje časové razítko, metodu, cestu a dotaz a nezpracované tělo. Ověřte tento podpis a odmítněte požadavky, jejichž t se liší od vašich hodin o více než 5 minut, aby zachycený požadavek nemohl být později znovu přehrán. Při opakovaném odeslání jsou pokusy podepsány znovu.
  • X-ThunderPhone-Signature podepisuje pouze nezpracované tělo. Stále se odesílá beze změny, aby stávající ověřovače nadále fungovaly.

Kroky

  1. Před jakýmkoli parsováním načtěte hlavičku, metodu, nezpracovanou cestu a dotaz a nezpracované tělo.
  2. Pomocí svého tajného klíče vypočítejte hexadecimální HMAC-SHA256 z <t>\n<METHOD>\n<path and query>\n<body>.
  3. Požadavek odmítněte, pokud se t liší o více než 300 sekund nebo pokud se výsledek při porovnání v konstantním čase neshoduje s v1.

Těla jsou kanonickou serializací JSON (seřazené klíče, kompaktní oddělovače) a podepisujeme přesně ty bajty, které přenášíme. Průvodce ověřením obsahuje přesný podepisovaný řetězec, praktický příklad, kód bez SDK a starší hlavičku.

Python (Flask)
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 "", 204
Node.js (Express)
import 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);
  },
);

Sémantika doručování

Tyto sémantiky se vztahují na doručování do endpointů. Starší webhook s jednou adresou URL provede jeden synchronní pokus bez opakování, s výjimkou výměny konfigurace telefonního hovoru telephony.incoming, která se opakuje jednou.

Opakování

Každá událost se ihned zkusí doručit jednou. Jakákoli odpověď 2xx potvrzuje doručení. Při jakémkoli jiném výsledku (jiné než 2xx, chyba připojení, časový limit) doručení zopakujeme 1 m, 5 m, 30 m, 2 h, 6 h, 12 h a 24 h po prvním pokusu — celkem 8 pokusů během 24 hodin. Pokud selžou všechny pokusy, doručování se zastaví a endpoint se v endpointech webhooků označí jako status="failing"; vlastníkům a administrátorům vaší organizace odešleme e-mail a zobrazíme to v dashboardu. Vraťte 2xx, jakmile je datová část trvale přijata; zpracujte ji asynchronně. Testovací odeslání z testovacího API endpointu dostanou jedno opakování přibližně minutu po prvním pokusu a nikdy nezmění stav endpointu.

Pořadí

Pořadí doručování není zaručeno. V praxi doručujeme události v pořadí, v jakém jsou emitovány, ale opakování může při selhání pořadí změnit. Vždy odstraňujte duplicity a slaďujte data podle call_id / ID objektu.

Duplicity

Doručování je alespoň jednou: opakování po odpovědi, kterou jsme neobdrželi, může vytvořit duplicitní událost. Každé opakování nese stejné event_id, proto ukládejte ID zpracovaných událostí a opakování přeskakujte. 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čení do endpointů mají časový limit 30 s na pokus. Ve starší cestě jsou blokující požadavky, které řídí chování probíhajícího hovoru — výměna konfigurace telephony.incoming / web.incoming — omezeny na 10 s pro první pokus telefonního hovoru a na 13 s pro celou výměnu včetně jednoho opakování a na 10 s pro relaci widgetu. 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 webhooku ve výchozím nastavení umožňuje 20 s a deklarace nástrojů mohou nastavit timeout nejvyšší úrovně.

Zdrojové IP adresy

Odchozí webhooky pocházejí z rozsahu cloudových IP adres ThunderPhone. Pokud váš firewall vyžaduje seznam povolených adres, kontaktujte podporu a my vám sdělíme aktuální rozsahy.

Volba mezi staršími webhooky a webhooky založenými na koncových bodech

FunkceStarší (/v1/webhook)Koncové body (/v1/developer/webhook-endpoints)
Počet 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é, kromě jednoho při výměně konfigurace telefonního hovoru8 pokusů během 24 h
Obálkatype + datatype + data + event_id
Rotace tajného klíčeNahrazuje jeden tajný klíčTajný klíč pro každý koncový bod
Zakázání bez odstraněníPUT /v1/webhook s {"url": ""}status=disabled
Viditelnost stavu—active / disabled / failing
Blokující výměna konfiguraceAno (telephony.incoming / web.incoming)Nikdy — pouze oznámení
Nejvhodnější proDynamickou konfiguraci hovorůZpracování událostí v produkci

Nové integrace by měly zpracovávat události prostřednictvím webhooků založených na koncových bodech. Starší adresu URL ponechte (nebo přidejte) pouze v případě, že hovory konfigurujete dynamicky při jejich přijetí nebo používáte odesílání nástrojů v režimu webhooků — tyto výměny požadavků a odpovědí fungují pouze ve starší cestě.


Související