ThunderPhone 2.0 är här.Kom igång på egen hand, från 2 ¢/min.Läs lanseringsnyheten

Webhooks

Översikt över webhooks

Hur ThunderPhone levererar händelser i realtid, hur du verifierar signaturer och hur de äldre och endpointbaserade leveransmodellerna jämförs.

ThunderPhone skickar HTTP-POST-begäranden till din server när saker händer under ett samtal — ett inkommande samtal startar, ett samtal avslutas, en utvärderingskörning slutförs, en avisering utlöses och så vidare. Det finns två leveransmodeller:

Alla listade händelsetyper i händelsekatalogen levereras via webhook-slutpunkter. De sex händelserna för samtalets livscykel (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) skickas också till den äldre webhooken med en URL — om du har både en äldre URL och en matchande slutpunkt får du händelsen på båda sökvägarna. Blockerande beteende (konfigurationsutbytet för telephony.incoming / web.incoming och verktygsdirigering i webhook-läge) finns endast på den äldre sökvägen; varje leverans till en slutpunkt är en skicka-och-glöm-avisering.

Payloadformat

Leveranser till slutpunkter är ett JSON-objekt med data, event_id och type:

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

event_id är unikt för varje skickad händelse. Det är identiskt vid återförsök och för varje slutpunkt som tar emot händelsen — deduplicera utifrån det.

Den äldre webhooken med en URL skickar samma type och data, men utan event_id:

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

Vid överföring serialiseras varje brödtext kanoniskt — nycklar sorteras alfabetiskt, utan blanksteg, UTF-8. De formaterade exemplen i den här dokumentationen är endast för läsbarhet.

Se händelsekatalogen för den fullständiga listan över händelsetyper och payloadfält.

Signaturverifiering

Varje begäran har två signaturer, båda HMAC-SHA256 med endpointens secret som nyckel (eller din organisations webhook-secret för äldre leveranser):

  • X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex> signerar tidsstämpeln, metoden, sökvägen och frågesträngen samt den råa brödtexten. Verifiera denna och avvisa begäranden vars t skiljer sig med mer än 5 minuter från din klocka, så att en uppfångad begäran inte kan spelas upp igen senare. Omförsök signeras på nytt när de skickas.
  • X-ThunderPhone-Signature signerar endast den råa brödtexten. Den skickas fortfarande oförändrad så att befintliga verifierare fortsätter att fungera.

Steg

  1. Läs headern, metoden, den råa sökvägen och frågesträngen samt den råa brödtexten före all tolkning.
  2. Beräkna hex-HMAC-SHA256 för <t>\n<METHOD>\n<path and query>\n<body> med din hemlighet.
  3. Avvisa begäran om t avviker med mer än 300 sekunder eller om resultatet inte matchar v1 i en jämförelse med konstant tid.

Brödtexter är den kanoniska JSON-serialiseringen (sorterade nycklar, kompakta avgränsare), och vi signerar exakt de byte vi överför. Guiden för verifiering innehåller den exakta signerade strängen, ett genomarbetat exempel, kod utan SDK:t och den äldre headern.

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

Leveranssemantik

Dessa semantiker gäller leveranser till slutpunkter. Den äldre webhooken med en enda URL är ett enda synkront försök utan återförsök, förutom konfigurationsutbytet för telefonsamtal telephony.incoming, som försöks igen en gång.

Återförsök

Varje händelse försöks levereras en gång omedelbart. Alla 2xx-svar bekräftar leveransen. Vid alla andra utfall (icke-2xx, anslutningsfel, tidsgräns) försöker vi igen efter 1 m, 5 m, 30 m, 2 h, 6 h, 12 h och 24 h från det första försöket — 8 försök under 24 timmar. Om alla försök misslyckas stoppas leveransen och slutpunkten markeras med status="failing" i webhookslutpunkter; vi skickar e-post till organisationens ägare och administratörer och visar det i instrumentpanelen. Returnera 2xx så snart nyttolasten har accepterats varaktigt; bearbeta den asynkront. Testsändningar från slutpunktens test-API får ett återförsök ungefär en minut efter det första försöket och ändrar aldrig slutpunktens status.

Ordning

Leveransordningen sker efter bästa förmåga. I praktiken levererar vi i den ordning händelser skapas, men återförsök kan ändra ordningen vid fel. Avduplicera och stäm alltid av med call_id / objekt-id.

Dubbletter

Leverans sker minst en gång: ett återförsök efter ett svar som vi aldrig såg kan duplicera en händelse. Varje återförsök har samma event_id, så lagra bearbetade id:n och hoppa över upprepningar. event_id delas även mellan ändpunkter — två ändpunkter som prenumererar på samma händelse får samma event_id.

Tidsgränser

Leveranser till slutpunkter har en tidsgräns på 30 s per försök. På den äldre sökvägen är blockerande förfrågningar som styr beteendet under pågående samtal — konfigurationsutbytet telephony.incoming / web.incoming — begränsade till 10 s för ett telefonsamtals första försök och 13 s för hela utbytet inklusive dess enda återförsök, samt till 10 s för en widgetsessions. Ett långsamt svar fördröjer besvarandet av samtalet, så försök svara inom ett par sekunder. Verktygsanrop i webhookläge tillåter 20 s som standard, och verktygsdeklarationer kan ange en timeout på toppnivå.

Käll-IP-adresser

Utgående webhooks kommer från ThunderPhones IP-intervall i molnet. Om din brandvägg kräver en tillåtelselista kontaktar du supporten så delar vi de aktuella intervallen.

Välja mellan äldre och ändpunktsbaserade webhooks

FunktionÄldre (/v1/webhook)Slutpunkter (/v1/developer/webhook-endpoints)
Antal URL:er1 per organisationFlera per organisation
HändelsetäckningEndast telephony.* / web.*Alla 10 händelsetyper
Händelsefilter—Per slutpunkt
ÅterförsökInga, förutom ett för konfigurationsutbytet för telefonsamtal8 försök under 24 h
Omslagtype + datatype + data + event_id
Rotering av hemlighetErsätter en enda hemlighetHemlighet per slutpunkt
Inaktivera utan att ta bortPUT /v1/webhook med {"url": ""}status=disabled
Statussynlighet—active / disabled / failing
Blockerande konfigurationsutbyteJa (telephony.incoming / web.incoming)Aldrig — endast notifieringar
Bäst förDynamisk samtalskonfigurationHändelsehantering i produktion

Nya integrationer bör hantera händelser via ändpunktsbaserade webhooks. Behåll (eller lägg till) en äldre URL endast om du konfigurerar samtal dynamiskt när de besvaras eller använder verktygsanrop i webhookläge — dessa begäran/svar-utbyten körs endast på den äldre sökvägen.


Relaterat