Open in
Panoramica dei webhook
Come ThunderPhone invia eventi in tempo reale, come verificare le firme e come si confrontano i modelli di consegna legacy e basati su endpoint.
ThunderPhone invia richieste HTTP POST al tuo server quando si verificano
eventi durante una chiamata: inizia una chiamata in entrata, termina una chiamata, viene completata
un'esecuzione di valutazione, viene attivato un avviso e così via. Esistono due modelli
di consegna:
Più URL, secret per endpoint, filtri evento per endpoint
e tentativi automatici.
Gestiscili tramite GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Un URL per organizzazione. Trasporta gli eventi del ciclo di vita della chiamata, inclusi gli
scambi di configurazione bloccanti. Gestito tramite GET/PUT /v1/webhook.
Tutti i tipi di evento elencati nel catalogo eventi vengono
consegnati tramite endpoint webhook. I sei eventi del ciclo di vita della chiamata
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) vengono inoltre inviati al
webhook legacy a URL singolo: se hai sia un URL legacy sia un endpoint
corrispondente, ricevi l'evento su entrambi i percorsi. Il comportamento bloccante
(lo scambio di configurazione telephony.incoming / web.incoming
e l'instradamento degli strumenti in modalità webhook)
risiede esclusivamente nel percorso legacy; ogni consegna a un endpoint è una
notifica fire-and-forget.
Formato del payload
Le consegne agli endpoint sono un oggetto JSON con data, event_id e
type:
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id è univoco per ogni evento emesso. È identico tra i tentativi
e tra tutti gli endpoint che ricevono l'evento: usalo per la deduplicazione.
Il webhook legacy a URL singolo invia gli stessi type e data, ma
senza event_id:
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}In transito, ogni body viene serializzato in modo canonico: chiavi ordinate alfabeticamente, nessuno spazio vuoto, UTF-8. Gli esempi formattati nelle presenti documentazioni sono solo a scopo di leggibilità.
Consulta il catalogo eventi per l'elenco completo dei tipi di evento e dei campi del payload.
Verifica della firma
Ogni richiesta include due firme, entrambe HMAC-SHA256 con chiave nel
secret dell'endpoint (oppure nel secret del webhook a livello di organizzazione per le
consegne legacy):
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>firma il timestamp, il metodo, il percorso e la query, nonché il corpo non elaborato. Verifica questa firma e rifiuta le richieste il cuitdista più di 5 minuti dal tuo orologio, in modo che una richiesta intercettata non possa essere riprodotta in seguito. I tentativi vengono firmati di nuovo quando vengono inviati.X-ThunderPhone-Signaturefirma solo il corpo non elaborato. Viene comunque inviata, invariata, affinché i verificatori esistenti continuino a funzionare.
Passaggi
- Leggi l'header, il metodo, il percorso e la query non elaborati e il corpo non elaborato prima di qualsiasi analisi.
- Calcola l'HMAC-SHA256 esadecimale di
<t>\n<METHOD>\n<path and query>\n<body>con il tuo segreto. - Rifiuta la richiesta se
tdifferisce di oltre 300 secondi o se il risultato non corrisponde av1in un confronto a tempo costante.
I corpi sono la serializzazione JSON canonica (chiavi ordinate, separatori compatti) e firmiamo esattamente i byte che trasmettiamo. La guida alla verifica contiene la stringa firmata esatta, un esempio completo, codice senza SDK e l'header legacy.
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 "", 204import 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);
},
);Semantica di consegna
Queste semantiche si applicano alle consegne degli endpoint. Il webhook legacy con un singolo URL
effettua un unico tentativo sincrono senza nuovi tentativi, fatta eccezione per lo scambio di configurazione
delle chiamate telefoniche telephony.incoming,
che viene ritentato una volta.
Nuovi tentativi
Ogni evento viene tentato immediatamente una volta. Qualsiasi risposta 2xx
conferma la consegna. In caso di qualsiasi altro esito (non-2xx,
errore di connessione, timeout), ritentiamo dopo 1 m, 5 m, 30 m, 2 h, 6 h,
12 h e 24 h dal primo tentativo — 8 tentativi nell'arco di
24 ore. Se tutti i tentativi falliscono, la consegna si interrompe e l'endpoint
viene contrassegnato con status="failing" negli
endpoint webhook; inviamo un'email ai
proprietari e agli amministratori della tua organizzazione e lo mostriamo nella dashboard. Restituisci 2xx non appena
il payload viene accettato in modo duraturo; elaboralo in modo asincrono.
Gli invii di test dall'API di test dell'endpoint ricevono un nuovo tentativo circa un minuto dopo
il primo tentativo e non modificano mai lo stato dell'endpoint.
Ordinamento
L'ordinamento delle consegne è best-effort. In pratica consegniamo gli eventi
nell'ordine in cui vengono emessi, ma i ritentativi possono modificarne l'ordine in caso di errore.
Esegui sempre deduplicazione e riconciliazione tramite call_id / id oggetto.
Duplicati
La consegna è almeno una volta: un ritentativo dopo una risposta che non abbiamo
ricevuto può duplicare un evento. Ogni ritentativo contiene lo stesso
event_id, quindi memorizza gli id elaborati e ignora le ripetizioni. event_id è
inoltre condiviso tra gli endpoint — due endpoint iscritti allo
stesso evento ricevono lo stesso event_id.
Timeout
Le consegne degli endpoint hanno un timeout di 30 s per tentativo. Nel
percorso legacy, le richieste bloccanti che determinano il comportamento delle chiamate in corso — lo
scambio di configurazione telephony.incoming / web.incoming
— sono limitate a 10 s per il primo tentativo di una chiamata telefonica
e a 13 s per l'intero scambio, incluso l'unico
nuovo tentativo, e a 10 s per una sessione widget. Una risposta lenta ritarda
la risposta alla chiamata, quindi cerca di rispondere entro un paio di secondi. L'invio di strumenti in modalità webhook consente 20 s
per impostazione predefinita e le dichiarazioni degli strumenti possono impostare un timeout di primo livello.
IP di origine
I webhook in uscita provengono dall'intervallo IP cloud di ThunderPhone. Se il tuo firewall richiede un elenco di autorizzazione, contatta il supporto e condivideremo gli intervalli attuali.
Scelta tra webhook legacy e basati su endpoint
| Funzionalità | Legacy (/v1/webhook) | Endpoint (/v1/developer/webhook-endpoints) |
|---|---|---|
| Numero di URL | 1 per organizzazione | Molti per organizzazione |
| Copertura degli eventi | Solo telephony.* / web.* | Tutti i 10 tipi di evento |
| Filtro eventi | — | Per endpoint |
| Tentativi | Nessuno, tranne uno per lo scambio di configurazione della chiamata telefonica | 8 tentativi nell'arco di 24 h |
| Busta | type + data | type + data + event_id |
| Rotazione del segreto | Sostituisce il singolo segreto | Segreto per endpoint |
| Disabilitare senza eliminare | PUT /v1/webhook con {"url": ""} | status=disabled |
| Visibilità dello stato | — | active / disabled / failing |
| Scambio di configurazione bloccante | Sì (telephony.incoming / web.incoming) | Mai — solo notifiche |
| Ideale per | Configurazione dinamica delle chiamate | Elaborazione degli eventi in produzione |
Le nuove integrazioni dovrebbero consumare gli eventi tramite webhook basati su endpoint. Mantieni (o aggiungi) un URL legacy solo se configuri le chiamate dinamicamente al momento della risposta o utilizzi l'invio di strumenti in modalità webhook — questi scambi richiesta/risposta vengono eseguiti solo sul percorso legacy.
Correlati
Tutti i tipi di evento e i relativi payload.
Gestisci più endpoint, filtri degli eventi e segreti.
La richiesta bloccante a cui il tuo server deve rispondere per configurare le chiamate.
Payload post-chiamata con trascrizione, registrazione e metriche.