ThunderPhone 2.0 je stigao.Postavite sve sami, već od 2 ¢/min.Pročitajte objavu

Webhooks

Pregled web-dojavnika

Kako ThunderPhone isporučuje događaje u stvarnom vremenu, kako provjeriti potpise te usporedba naslijeđenog modela isporuke i modela isporuke temeljenog na krajnjim točkama.

ThunderPhone šalje HTTP POST zahtjeve vašem poslužitelju kada se tijekom poziva nešto dogodi — započne dolazni poziv, poziv završi, dovrši se pokretanje ocjenjivanja, aktivira se upozorenje i slično. Postoje dva modela isporuke:

Svih deset vrsta događaja iz kataloga događaja isporučuje se putem krajnjih točaka webhooka. Šest događaja životnog ciklusa poziva (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) također se šalje na naslijeđeni webhook s jednim URL-om — ako imate i naslijeđeni URL i odgovarajuću krajnju točku, događaj primate putem obje putanje. Blokirajuće ponašanje (razmjena konfiguracije za telephony.incoming / web.incoming i slanje alata u načinu rada webhooka) postoji isključivo na naslijeđenoj putanji; svaka isporuka krajnjoj točki obavijest je bez čekanja odgovora.

Format podataka

Isporuke krajnjoj točki JSON su objekt s poljima data, event_id i type:

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

event_id jedinstven je za svaki emitirani događaj. Isti je pri ponovnim pokušajima i na svakoj krajnjoj točki koja primi događaj — upotrijebite ga za uklanjanje duplikata.

Naslijeđeni webhook s jednim URL-om šalje isti type i data, ali bez polja event_id:

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

Pri prijenosu se svako tijelo serijalizira kanonski — ključevi su poredani abecedno, bez razmaka, u UTF-8 kodiranju. Primjeri s oblikovanim prikazom u ovoj dokumentaciji služe samo za čitljivost.

Pogledajte Katalog događaja za potpuni popis vrsta događaja i polja podataka.

Provjera potpisa

Svaki zahtjev nosi HMAC-SHA256 potpis nad izvornim tijelom zahtjeva u zaglavlju X-ThunderPhone-Signature. Ključ za potpisivanje je secret krajnje točke (ili secret vašeg webhooka na razini organizacije za naslijeđene isporuke).

Koraci

  1. Pročitajte izvorno tijelo zahtjeva prije bilo kakvog parsiranja.
  2. Izračunajte hmac_sha256(secret, body).hexdigest().
  3. Usporedite u konstantnom vremenu sa zaglavljem X-ThunderPhone-Signature.

Potpisujemo točno bajtove koje šaljemo, a ti bajtovi predstavljaju kanonsku JSON serijalizaciju (sortirani ključevi, sažeti razdjelnici). Stoga provjera prema izvornom tijelu uvijek funkcionira — a ako vam vaš okvir prosljeđuje samo parsirani JSON, ponovna serijalizacija sa sortiranim ključevima i sažetim razdjelnicima stvara identične bajtove. Obje su metode obuhvaćene u vodiču za provjeru.

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

Semantika isporuke

Ova semantika primjenjuje se na isporuke na krajnje točke. Naslijeđeni webhook s jednim URL-om jedan je sinkroni pokušaj bez ponavljanja.

Ponovni pokušaji

Svaki se događaj pokušava isporučiti jednom odmah. Svaki odgovor 2xx potvrđuje isporuku. Za svaki drugi ishod (koji nije 2xx, pogreška veze, vremensko ograničenje) ponavljamo pokušaj 1 min, 5 min, 30 min, 2 h, 6 h, 12 h i 24 h nakon prvog pokušaja — 8 pokušaja tijekom 24 sata. Ako svaki pokušaj ne uspije, isporuka se zaustavlja, a krajnja točka označava se kao status="failing" u krajnjim točkama webhooka. Vratite 2xx čim je payload trajno prihvaćen; obradite ga asinkrono.

Redoslijed

Redoslijed isporuke temelji se na najboljem mogućem nastojanju. U praksi isporučujemo redoslijedom kojim se događaji emitiraju, ali ponovni pokušaji mogu promijeniti redoslijed nakon neuspjeha. Uvijek uklonite duplikate i uskladite podatke prema call_id / ID-u objekta.

Duplikati

Isporuka je najmanje jednom: ponovni pokušaj nakon odgovora koji nikada nismo primili može duplicirati događaj. Svaki ponovni pokušaj nosi isti event_id, stoga pohranite obrađene ID-ove i preskočite ponavljanja. event_id je također zajednički za krajnje točke — dvije krajnje točke pretplaćene na isti događaj primaju isti event_id.

Vremenska ograničenja

Isporuke na krajnje točke imaju vremensko ograničenje od 30 s po pokušaju. Na naslijeđenoj putanji, blokirajući zahtjevi koji upravljaju ponašanjem aktivnog poziva — razmjena konfiguracije telephony.incoming / web.incoming — istječu nakon 10 s, ali spor odgovor odgađa javljanje na poziv, stoga odgovorite u roku od nekoliko sekundi. Slanje alata u načinu webhooka slanje alata prema zadanim postavkama dopušta 20 s, a deklaracije alata mogu postaviti timeout na najvišoj razini.

Izvorne IP adrese

Odlazni webhookovi dolaze iz raspona IP adresa ThunderPhone clouda. Ako vaš vatrozid zahtijeva popis dopuštenih adresa, obratite se podršci i podijelit ćemo trenutačne raspone.

Odabir između naslijeđenih webhookova i webhookova temeljenih na krajnjim točkama

ZnačajkaNaslijeđeni (/v1/webhook)Krajnje točke (/v1/developer/webhook-endpoints)
Broj URL-ova1 po organizacijiViše po organizaciji
Obuhvat događajaSamo telephony.* / web.*Svih 10 vrsta događaja
Filtar događajaPo krajnjoj točki
Ponovni pokušajiNema8 pokušaja tijekom 24 h
Omotnicatype + datatype + data + event_id
Rotacija tajneZamjenjuje jednu tajnuTajna po krajnjoj točki
Onemogućavanje bez brisanjaPUT /v1/webhook s {"url": ""}status=disabled
Vidljivost statusaactive / disabled / failing
Blokirajuća razmjena konfiguracijeDa (telephony.incoming / web.incoming)Nikada — samo obavijesti
Najbolje zaDinamičku konfiguraciju pozivaKorištenje događaja u produkciji

Nove integracije trebaju primati događaje putem webhookova temeljenih na krajnjim točkama. Zadržite (ili dodajte) naslijeđeni URL samo ako dinamički konfigurirate pozive u trenutku javljanja ili koristite slanje alata u načinu webhooka — te razmjene zahtjeva i odgovora rade samo na naslijeđenoj putanji.


Povezano