Webhookų apžvalga

ThunderPhone siunčia HTTP POST užklausas į jūsų serverį, kai skambučio metu įvyksta tam tikri veiksmai – pradedamas įeinantis skambutis, baigiamas skambutis, užbaigiama vertinimo vykdymo užduotis, suveikia įspėjimas ir pan. Yra du pristatymo modeliai:

Visi dešimt įvykių tipų įvykių kataloge pristatomi per webhook galinius taškus. Šeši skambučio ciklo įvykiai (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) taip pat siunčiami į pasenusį vieno URL webhook – jei turite ir pasenusį URL, ir atitinkamą galinį tašką, įvykį gaunate abiem keliais. Blokavimo veiksmai (telephony.incoming / web.incoming konfigūracijos mainai ir webhook režimo įrankių paskirstymas) vykdomi išskirtinai pasenusiu keliu; kiekvienas pristatymas į galinį tašką yra pranešimas, siunčiamas nelaukiant atsakymo.

Duomenų formato struktūra

Pristatymas į galinį tašką yra JSON objektas su data, event_id ir type:

{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}

event_id yra unikalus kiekvienam išsiųstam įvykiui. Jis nesikeičia per pakartotinius bandymus ir visuose galiniuose taškuose, kurie gauna įvykį – naudokite jį dublikatams pašalinti.

Pasenęs vieno URL webhook siunčia tuos pačius type ir data, bet be event_id:

{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}

Perduodant tinklu, kiekvienas turinys yra kanoniškai serializuojamas – raktai surikiuojami abėcėlės tvarka, be tarpų, UTF-8 koduote. Šioje dokumentacijoje pateikti išdėstyti pavyzdžiai skirti tik skaitomumui.

Visą įvykių tipų ir duomenų laukų sąrašą rasite įvykių kataloge.

Parašo patvirtinimas

Kiekvienoje užklausoje antraštėje X-ThunderPhone-Signature pateikiamas HMAC-SHA256 parašas, apskaičiuotas pagal neapdorotą užklausos turinį. Pasirašymo raktas yra galinio taško secret (arba jūsų organizacijos lygio žiniatinklio kablio secret, skirtas senesniems siuntimams).

Veiksmai

  1. Nuskaitykite neapdorotą užklausos turinį prieš bet kokį analizavimą.
  2. Apskaičiuokite hmac_sha256(secret, body).hexdigest().
  3. Pastoviuoju laiku palyginkite su antrašte X-ThunderPhone-Signature.

Pasirašome tiksliai tuos baitus, kuriuos perduodame, o tie baitai yra kanoninis JSON serializavimas (surikiuoti raktai, glausti skirtukai). Todėl patvirtinimas pagal neapdorotą turinį visada veikia — o jei jūsų sistema pateikia tik išanalizuotą JSON, pakartotinis jo serializavimas su surikiuotais raktais ir glaustais skirtukais sukuria identiškus baitus. Abu būdai aprašyti patvirtinimo vadove.

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);
  },
);

Pristatymo semantika

Ši semantika taikoma pristatymams į galinius taškus. Senasis vieno URL žiniatinklio kabliukas yra vienas sinchroninis bandymas be pakartotinių bandymų.

Pakartotiniai bandymai

Kiekvienas įvykis iš karto bandomas pristatyti vieną kartą. Bet koks 2xx atsakymas patvirtina pristatymą. Esant bet kokiai kitai baigčiai (ne 2xx, ryšio klaida, laiko limitas), bandome dar kartą praėjus 1 min., 5 min., 30 min., 2 val., 6 val., 12 val. ir 24 val. po pirmojo bandymo — iš viso 8 bandymai per 24 valandas. Jei nepavyksta kiekvieno bandymo, pristatymas sustabdomas, o galinis taškas žiniatinklio kabliukų galiniuose taškuose pažymimas būsena status="failing". Grąžinkite 2xx iškart, kai naudingasis krovinys patikimai priimtas; apdorokite asinchroniškai.

Eiliškumas

Pristatymo eiliškumas užtikrinamas pagal geriausias pastangas. Praktikoje pristatome tokia tvarka, kuria įvykiai sugeneruojami, tačiau nesėkmės atveju pakartotiniai bandymai gali pakeisti eilę. Visada pašalinkite dublikatus ir sulyginkite pagal call_id / objekto id.

Dublikatai

Pristatymas vyksta bent kartą: pakartotinis bandymas po atsakymo, kurio negavome, gali dubliuoti įvykį. Kiekvienas pakartotinis bandymas turi tą patį event_id, todėl saugokite apdorotus id ir praleiskite pasikartojimus. event_id taip pat bendrinamas tarp galinių taškų — du galiniai taškai, užsiprenumeravę tą patį įvykį, gauna tą patį event_id.

Laiko limitai

Pristatymams į galinius taškus taikomas 30 s laiko limitas kiekvienam bandymui. Senajame kelyje blokuojančių užklausų, kurios lemia tiesioginio skambučio veikimą — telephony.incoming / web.incoming konfigūracijos apsikeitimo — laiko limitas yra 10 s, tačiau lėtas atsakymas atideda skambučio atsiliepimą, todėl siekite atsakyti per kelias sekundes. Žiniatinklio kabliuko režimo įrankių iškvietimas leidžia 20 s.

Šaltinio IP adresai

Išeinantys žiniatinklio kabliukai siunčiami iš ThunderPhone debesijos IP adresų diapazono. Jei jūsų užkardai reikalingas leidžiamųjų sąrašas, susisiekite su palaikymo komanda ir mes pateiksime dabartinius diapazonus.

Pasirinkimas tarp senųjų ir galinių taškų žiniatinklio kabliukų

FunkcijaSenasis (/v1/webhook)Galiniai taškai (/v1/developer/webhook-endpoints)
URL skaičius1 organizacijaiDaug organizacijai
Įvykių aprėptisTik telephony.* / web.*Visi 10 įvykių tipų
Įvykių filtrasKiekvienam galiniam taškui
Pakartotiniai bandymaiNėra8 bandymai per 24 val.
Apvalkalastype + datatype + data + event_id
Slaptojo rakto keitimasPakeičia vieną slaptąjį raktąSlaptasis raktas kiekvienam galiniam taškui
Išjungimas neištrinantstatus=disabled
Būsenos matomumasactive / disabled / failing
Blokuojantis konfigūracijos apsikeitimasTaip (telephony.incoming / web.incoming)Niekada — tik pranešimai
Geriausiai tinkaDinaminei skambučių konfigūracijaiĮvykių naudojimui produkcinėje aplinkoje

Naujos integracijos turėtų naudoti įvykius per galinių taškų žiniatinklio kabliukus. Išlaikykite (arba pridėkite) senąjį URL tik jei dinamiškai konfigūruojate skambučius atsiliepimo metu arba naudojate žiniatinklio kabliuko režimo įrankių iškvietimą — šie užklausos / atsakymo apsikeitimai vykdomi tik senuoju keliu.


Susiję