ThunderPhone 2.0 er lansert.Kom i gang selv, fra 2 ¢/min.Les mer om lanseringen

Webhooks

Oversikt over webhooks

Slik leverer ThunderPhone sanntidshendelser, slik verifiserer du signaturer, og slik sammenlignes de eldre og endepunktbaserte leveringsmodellene.

ThunderPhone sender HTTP-POST-forespørsler til serveren din når noe skjer under en samtale — et innkommende anrop starter, en samtale avsluttes, en evalueringskjøring fullføres, et varsel utløses og så videre. Det finnes to leveringsmodeller:

Alle de ti hendelsestypene i hendelseskatalogen leveres gjennom webhook-endepunkter. De seks hendelsene i samtalens livssyklus (telephony.incoming, telephony.complete, telephony.tool, web.incoming, web.complete, web.tool) sendes også til den eldre webhooken med én URL — hvis du har både en eldre URL og et samsvarende endepunkt, mottar du hendelsen på begge banene. Blokkerende atferd (konfigurasjonsutvekslingen for telephony.incoming / web.incoming og verktøyutsending i webhook-modus) finnes utelukkende på den eldre banen; hver levering til et endepunkt er et varsel uten venting på svar.

Nyttelastformat

Leveringer til endepunkter er et JSON-objekt med data, event_id og type:

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

event_id er unik for hver utsendte hendelse. Den er identisk på tvers av nye forsøk og på tvers av alle endepunkter som mottar hendelsen — bruk den til deduplisering.

Den eldre webhooken med én URL sender samme type og data, men uten event_id:

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

Ved overføring serialiseres hver brødtekst kanonisk — nøkler sorteres alfabetisk, uten mellomrom, UTF-8. De pent formaterte eksemplene i denne dokumentasjonen er kun for lesbarhet.

Se hendelseskatalogen for en fullstendig liste over hendelses- typer og nyttelastfelt.

Signaturverifisering

Hver forespørsel har en HMAC-SHA256-signatur over den rå forespørselsbrødteksten i X-ThunderPhone-Signature-headeren. Signeringsnøkkelen er endepunktets secret (eller webhookens secret på organisasjonsnivå for eldre leveringer).

Trinn

  1. Les den rå forespørselsbrødteksten før eventuell parsing.
  2. Beregn hmac_sha256(secret, body).hexdigest().
  3. Sammenlign i konstant tid med X-ThunderPhone-Signature-headeren.

Vi signerer nøyaktig byteene vi overfører, og disse byteene er den kanoniske JSON-serialiseringen (sorterte nøkler, kompakte skilletegn). Derfor fungerer verifisering mot råbrødteksten alltid — og hvis rammeverket ditt bare gir deg parsede JSON-data, produserer reserialisering med sorterte nøkler og kompakte skilletegn identiske byte. Begge fremgangsmåtene er dekket i verifiseringsveiledningen.

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

Leveringssemantikk

Denne semantikken gjelder leveringer til endepunkt. Den eldre webhooken med én URL er ett enkelt synkront forsøk uten nye forsøk.

Nye forsøk

Hver hendelse forsøkes levert én gang umiddelbart. Ethvert 2xx-svar bekrefter leveringen. Ved ethvert annet utfall (ikke-2xx, tilkoblingsfeil, tidsavbrudd) prøver vi på nytt 1 min, 5 min, 30 min, 2 t, 6 t, 12 t og 24 t etter første forsøk — 8 forsøk fordelt over 24 timer. Hvis alle forsøk mislykkes, stopper leveringen, og endepunktet markeres med status="failing" i webhook-endepunkter. Returner 2xx så snart nyttelasten er varig mottatt; behandle den asynkront.

Rekkefølge

Leveringsrekkefølgen er etter beste evne. I praksis leverer vi i rekkefølgen hendelser sendes ut, men nye forsøk kan endre rekkefølgen ved feil. Dedupliser alltid og avstem etter call_id / objekt-ID.

Duplikater

Levering er minst én gang: et nytt forsøk etter et svar vi aldri mottok, kan duplisere en hendelse. Hvert nytt forsøk har samme event_id, så lagre behandlede ID-er og hopp over gjentakelser. event_id er også delt på tvers av endepunkter — to endepunkter som abonnerer på den samme hendelsen, mottar samme event_id.

Tidsavbrudd

Leveringer til endepunkter har et tidsavbrudd på 30 s per forsøk. På den eldre ruten har blokkerende forespørsler som styrer aktive samtaler — konfigurasjonsutvekslingen telephony.incoming / web.incoming — tidsavbrudd etter 10 s, men et tregt svar forsinker besvarelsen av samtalen, så prøv å svare innen et par sekunder. Verktøydistribuering i webhook-modus tillater 20 s som standard, og verktøydeklarasjoner kan angi et timeout på øverste nivå.

Kilde-IP-adresser

Utgående webhooks kommer fra ThunderPhones IP-område i skyen. Hvis brannmuren din krever en tillatelsesliste, kan du kontakte support, så deler vi de gjeldende områdene.

Velge mellom eldre og endepunktbaserte webhooks

FunksjonEldre (/v1/webhook)Endepunkter (/v1/developer/webhook-endpoints)
Antall URL-er1 per organisasjonMange per organisasjon
HendelsesdekningBare telephony.* / web.*Alle 10 hendelsestyper
HendelsesfilterPer endepunkt
Nye forsøkIngen8 forsøk over 24 t
Innpakningtype + datatype + data + event_id
Rotering av hemmelig nøkkelErstatter én hemmelig nøkkelHemmelig nøkkel per endepunkt
Deaktiver uten å slettestatus=disabled
Synlighet for statusactive / disabled / failing
Blokkerende konfigurasjonsutvekslingJa (telephony.incoming / web.incoming)Aldri — kun varsler
Best forDynamisk samtalekonfigurasjonHendelseshåndtering i produksjon

Nye integrasjoner bør motta hendelser gjennom endepunktbaserte webhooks. Behold (eller legg til) en eldre URL bare hvis du konfigurerer samtaler dynamisk ved besvarelse eller bruker verktøydistribuering i webhook-modus — disse forespørsels-/svarsutvekslingene kjører bare på den eldre ruten.


Relatert