ThunderPhone 2.0 är här.Kom igång själv, från 2 cent/minut.Läs lanseringsnyheten

Webhooks

Översikt över webhooks

Så levererar ThunderPhone händelser i realtid, så verifierar du signaturer och så jämförs de äldre och slutpunktsbaserade leveransmodellerna.

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 tio 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 även 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 (telephony.incoming / web.incoming-konfigurationsutbytet och verktygsdirigering i webhook-läge) finns endast på den äldre sökvägen; varje leverans till en slutpunkt är en envägsavisering.

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 utsänd 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 en fullständig lista över händelsetyper och payloadfält.

Signaturverifiering

Varje begäran innehåller en HMAC-SHA256-signatur över den råa begärandetexten i rubriken X-ThunderPhone-Signature. Signeringsnyckeln är slutpunktens secret (eller din organisationsövergripande webhook-secret för äldre leveranser).

Steg

  1. Läs den råa begärandetexten före all tolkning.
  2. Beräkna hmac_sha256(secret, body).hexdigest().
  3. Jämför med rubriken X-ThunderPhone-Signature i konstant tid.

Vi signerar exakt de byte vi överför, och dessa byte är den kanoniska JSON-serialiseringen (sorterade nycklar, kompakta avgränsare). Därför fungerar verifiering mot den råa texten alltid — och om ditt ramverk endast ger dig tolkad JSON, producerar en ny serialisering med sorterade nycklar och kompakta avgränsare identiska byte. Båda metoderna beskrivs i verifieringsguiden.

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

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.

Återförsök

Varje händelse försöks levereras en gång direkt. Alla 2xx-svar bekräftar leveransen. Vid alla andra utfall (icke-2xx, anslutningsfel, tidsgräns) försöker vi igen 1 min, 5 min, 30 min, 2 tim, 6 tim, 12 tim och 24 tim efter 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. Returnera 2xx så snart som nyttolasten har accepterats varaktigt; bearbeta den asynkront.

Ordning

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

Dubbletter

Leverans sker minst en gång: ett återförsök efter ett svar som vi aldrig tog emot kan skapa en dubblett av en händelse. Varje återförsök innehåller samma event_id, så lagra bearbetade id:n och hoppa över upprepningar. event_id delas också mellan slutpunkter — två slutpunkter 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 får blockerande begäranden som styr beteendet för aktiva samtal — konfigurationsutbytet för telephony.incoming / web.incoming — tidsgräns efter 10 s, men ett långsamt svar fördröjer besvarandet av samtalet, så sikta på att svara inom ett par sekunder. Verktygsdirigering i webhook-lä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 slutpunktsbaserade 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ändelsefilterPer slutpunkt
ÅterförsökInga8 försök under 24 tim
Kuverttype + datatype + data + event_id
HemlighetsrotationErsätter en enda hemlighetHemlighet per slutpunkt
Inaktivera utan att raderastatus=disabled
Statusvisningactive / disabled / failing
Blockerande konfigurationsutbyteJa (telephony.incoming / web.incoming)Aldrig — endast aviseringar
Bäst förDynamisk samtalskonfigurationHändelsehantering i produktion

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


Relaterat