ThunderPhone 2.0 este acum disponibil.Îl configurați singur, de la 2 ¢/min.Citiți anunțul

Webhooks

Prezentare generală a webhook-urilor

Cum livrează ThunderPhone evenimente în timp real, cum să verificați semnăturile și cum se compară modelele de livrare vechi și bazat pe endpoint-uri.

ThunderPhone trimite cereri HTTP POST către serverul dumneavoastră atunci când au loc evenimente în timpul unui apel — începe un apel de intrare, se încheie un apel, se finalizează o rulare de evaluare, se declanșează o alertă și așa mai departe. Există două modele de livrare:

Toate cele zece tipuri de evenimente din catalogul de evenimente sunt livrate prin endpointuri webhook. Cele șase evenimente din ciclul de viață al apelului (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) sunt trimise și către webhook-ul moștenit cu un singur URL — dacă aveți atât un URL moștenit, cât și un endpoint corespunzător, primiți evenimentul pe ambele căi. Comportamentul blocant (schimbul de configurare telephony.incoming / web.incoming și expedierea instrumentelor în modul webhook) există exclusiv pe calea moștenită; fiecare livrare către endpoint este o notificare fire-and-forget.

Formatul încărcăturii utile

Livrările către endpoint sunt un obiect JSON cu 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 este unic pentru fiecare eveniment emis. Este identic între reîncercări și între toate endpointurile care primesc evenimentul — deduplicați pe baza acestuia.

Webhook-ul moștenit cu un singur URL trimite aceleași type și data, dar fără event_id:

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

La transmitere, fiecare corp este serializat canonic — cheile sunt sortate alfabetic, fără spații albe, UTF-8. Exemplele formatate pentru lizibilitate din această documentație sunt doar pentru claritate.

Consultați catalogul de evenimente pentru lista completă de tipuri de evenimente și câmpuri ale încărcăturii utile.

Verificarea semnăturii

Fiecare solicitare include o semnătură HMAC-SHA256 calculată asupra corpului brut al solicitării în antetul X-ThunderPhone-Signature. Cheia de semnare este secret al endpointului (sau secret webhook la nivel de organizație pentru livrările vechi).

Pași

  1. Citiți corpul brut al solicitării înainte de orice analiză.
  2. Calculați hmac_sha256(secret, body).hexdigest().
  3. Comparați în timp constant cu antetul X-ThunderPhone-Signature.

Semnăm exact octeții pe care îi transmitem, iar acești octeți reprezintă serializarea JSON canonică (chei sortate, separatori compacți). Prin urmare, verificarea față de corpul brut funcționează întotdeauna — iar dacă frameworkul vă oferă doar JSON analizat, reserializarea acestuia cu chei sortate și separatori compacți produce octeți identici. Ambele metode sunt prezentate în ghidul de verificare.

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

Semantica livrării

Această semantică se aplică livrărilor către endpoint-uri. Webhook-ul moștenit cu un singur URL efectuează o singură încercare sincronă, fără reîncercări.

Reîncercări

Fiecare eveniment este încercat imediat o dată. Orice răspuns 2xx confirmă livrarea. Pentru orice alt rezultat (non-2xx, eroare de conexiune, expirare) reîncercăm la 1 min, 5 min, 30 min, 2 h, 6 h, 12 h și 24 h după prima încercare — 8 încercări pe parcursul a 24 de ore. Dacă toate încercările eșuează, livrarea se oprește, iar endpoint-ul este marcat cu status="failing" în endpoint-uri webhook. Returnați 2xx de îndată ce payload-ul este acceptat în mod durabil; procesați asincron.

Ordonare

Ordonarea livrărilor se face pe baza celui mai bun efort. În practică, livrăm în ordinea în care sunt emise evenimentele, însă reîncercările pot reordona livrările în caz de eșec. Eliminați întotdeauna duplicatele și reconciliați după call_id / id-ul obiectului.

Duplicate

Livrarea este cel puțin o dată: o reîncercare după un răspuns pe care nu l-am primit poate duplica un eveniment. Fiecare reîncercare poartă același event_id, așadar stocați id-urile procesate și omiteți repetările. event_id este partajat și între endpoint-uri — două endpoint-uri abonate la același eveniment primesc același event_id.

Expirări

Livrările către endpoint-uri au o expirare de 30 s pentru fiecare încercare. Pe calea moștenită, solicitările blocante care controlează comportamentul apelurilor active — schimbul de configurare telephony.incoming / web.incoming — expiră după 10 s, însă un răspuns lent întârzie preluarea apelului, așa că urmăriți să răspundeți în câteva secunde. Expedierea instrumentelor în modul webhook permite implicit 20 s, iar declarațiile de instrumente pot seta un timeout la nivel superior.

IP-uri sursă

Webhook-urile de ieșire provin din intervalul de IP-uri cloud al ThunderPhone. Dacă firewall-ul dumneavoastră necesită o listă de permisiuni, contactați asistența, iar noi vă vom comunica intervalele actuale.

Alegerea între webhook-uri moștenite și bazate pe endpoint-uri

FuncționalitateMoștenit (/v1/webhook)Endpoint-uri (/v1/developer/webhook-endpoints)
Număr de URL-uri1 per organizațieMai multe per organizație
Acoperire evenimenteDoar telephony.* / web.*Toate cele 10 tipuri de evenimente
Filtru de evenimentePentru fiecare endpoint
ReîncercăriNiciuna8 încercări în 24 h
Plictype + datatype + data + event_id
Rotirea secretuluiÎnlocuiește secretul unicSecret pentru fiecare endpoint
Dezactivare fără ștergerestatus=disabled
Vizibilitatea stăriiactive / disabled / failing
Schimb de configurare blocantDa (telephony.incoming / web.incoming)Niciodată — doar notificări
Cel mai potrivit pentruConfigurarea dinamică a apelurilorConsumul de evenimente în producție

Integrările noi ar trebui să consume evenimente prin webhook-uri bazate pe endpoint-uri. Păstrați (sau adăugați) un URL moștenit numai dacă configurați apelurile dinamic la momentul preluării sau utilizați expedierea instrumentelor în modul webhook — aceste schimburi cerere/răspuns rulează numai pe calea moștenită.


Asociate