Open in
Vue d’ensemble des webhooks
Comment ThunderPhone transmet les événements en temps réel, comment vérifier les signatures et comment les modèles de livraison hérités et basés sur les endpoints se comparent.
ThunderPhone envoie des requêtes HTTP POST à votre serveur lorsqu'un événement
se produit pendant un appel : un appel entrant démarre, un appel se termine, une exécution
d'évaluation se termine, une alerte se déclenche, etc. Il existe deux modèles
de livraison :
Plusieurs URL, des secrets par point de terminaison, des filtres d'événements par point de terminaison
et des tentatives automatiques.
Gérez-les via GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints.
Une URL par organisation. Transmet les événements du cycle de vie des appels, y compris les
échanges de configuration bloquants. Géré via GET/PUT /v1/webhook.
Tous les types d'événements répertoriés dans le catalogue des événements sont
transmis via des points de terminaison webhook. Les six événements du cycle de vie des appels
(telephony.incoming, telephony.complete, telephony.tool,
web.incoming, web.complete, web.tool) sont également envoyés au
webhook hérité à URL unique. Si vous disposez à la fois d'une URL héritée et d'un point de terminaison correspondant, vous recevez l'événement sur les deux
chemins. Le comportement bloquant (l'échange de configuration
telephony.incoming / web.incoming et le
répartiteur d'outils en mode webhook) se trouve
exclusivement sur le chemin hérité ; chaque livraison vers un point de terminaison est une
notification envoyée sans attente de réponse.
Format de la charge utile
Les livraisons vers les points de terminaison sont un objet JSON avec data, event_id et
type :
{
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
},
"event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
"type": "telephony.incoming"
}event_id est unique pour chaque événement émis. Il est identique lors des tentatives
et pour chaque point de terminaison qui reçoit l'événement : dédupliquez sur cette valeur.
Le webhook hérité à URL unique envoie les mêmes type et data, mais
sans event_id :
{
"type": "telephony.incoming",
"data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}Sur le réseau, chaque corps est sérialisé de manière canonique : clés triées par ordre alphabétique, sans espaces, en UTF-8. Les exemples mis en forme dans cette documentation sont fournis uniquement pour faciliter la lecture.
Consultez le catalogue des événements pour obtenir la liste complète des types d'événements et des champs de charge utile.
Vérification de signature
Chaque requête contient deux signatures, toutes deux HMAC-SHA256 utilisant le
secret du point de terminaison comme clé (ou le secret webhook au niveau de votre organisation pour les
livraisons héritées) :
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>signe l’ horodatage, la méthode, le chemin et la requête, ainsi que le corps brut. Vérifiez celle-ci et rejetez les requêtes donttdiffère de plus de 5 minutes de votre horloge afin qu’une requête interceptée ne puisse pas être rejouée ultérieurement. Les tentatives sont signées à nouveau lors de leur envoi.X-ThunderPhone-Signaturesigne uniquement le corps brut. Elle est toujours envoyée, sans modification, afin que les vérificateurs existants continuent de fonctionner.
Étapes
- Lisez l’en-tête, la méthode, le chemin et la requête bruts, ainsi que le corps brut avant toute analyse.
- Calculez le HMAC-SHA256 hexadécimal de
<t>\n<METHOD>\n<path and query>\n<body>avec votre secret. - Rejetez la requête si
tdiffère de plus de 300 secondes, ou si le résultat ne correspond pas àv1lors d’une comparaison en temps constant.
Les corps utilisent la sérialisation JSON canonique (clés triées, séparateurs compacts), et nous signons exactement les octets que nous transmettons. Le guide de vérification contient la chaîne signée exacte, un exemple détaillé, du code sans le SDK et l’en-tête historique.
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);
},
);Sémantique de livraison
Ces sémantiques s’appliquent aux livraisons de points de terminaison. Le
webhook hérité à URL unique effectue une seule tentative synchrone sans nouvelle tentative, à l’exception de l’échange de configuration d’appel téléphonique
telephony.incoming,
qui fait l’objet d’une nouvelle tentative.
Nouvelles tentatives
Chaque événement fait immédiatement l’objet d’une tentative. Toute réponse 2xx
confirme la livraison. Pour tout autre résultat (non-2xx,
erreur de connexion, délai d’expiration), nous effectuons une nouvelle tentative 1 min, 5 min, 30 min, 2 h, 6 h,
12 h et 24 h après la première tentative — soit 8 tentatives sur
24 heures. Si toutes les tentatives échouent, la livraison s’arrête et le point de terminaison
est marqué status="failing" dans les
points de terminaison de webhook ; nous envoyons un e-mail aux
propriétaires et administrateurs de votre organisation et l’affichons dans le tableau de bord. Renvoyez 2xx dès que
la charge utile est acceptée de manière durable ; traitez-la de manière asynchrone.
Les envois de test depuis l’API de test du point de terminaison bénéficient d’une nouvelle tentative environ une minute après
la première tentative et ne modifient jamais l’état du point de terminaison.
Ordre
L’ordre de livraison est fourni au mieux. En pratique, nous livrons les événements dans
l’ordre de leur émission, mais les nouvelles tentatives peuvent les réordonner en cas d’échec.
Dédupliquez et rapprochez toujours par call_id / ID d’objet.
Doublons
La livraison est au moins une fois : une nouvelle tentative après une réponse que nous n’avons jamais
reçue peut dupliquer un événement. Chaque nouvelle tentative contient le même
event_id ; stockez donc les ID traités et ignorez les répétitions. event_id est
également partagé entre les points de terminaison — deux points de terminaison abonnés au
même événement reçoivent le même event_id.
Délais d’expiration
Les livraisons de points de terminaison ont un délai d’expiration de 30 s par tentative. Sur le
chemin hérité, les requêtes bloquantes qui pilotent le comportement des appels en direct — l’échange de
configuration telephony.incoming / web.incoming
— sont limitées à 10 s pour la première tentative d’un appel téléphonique
et à 13 s pour l’ensemble de l’échange, y compris son unique
nouvelle tentative, et à 10 s pour une session de widget. Une réponse lente retarde
la prise d’appel ; visez donc une réponse en quelques secondes. La répartition des outils en mode webhook autorise 20 s
par défaut, et les déclarations d’outils peuvent définir un timeout de niveau supérieur.
IP source
Les webhooks sortants proviennent de la plage d’IP cloud de ThunderPhone. Si votre pare-feu exige une liste d’autorisation, contactez le support et nous vous communiquerons les plages actuelles.
Choisir entre les webhooks historiques et ceux basés sur des points de terminaison
| Fonctionnalité | Hérité (/v1/webhook) | Points de terminaison (/v1/developer/webhook-endpoints) |
|---|---|---|
| Nombre d’URL | 1 par organisation | Plusieurs par organisation |
| Couverture des événements | telephony.* / web.* uniquement | Les 10 types d’événements |
| Filtre d’événements | — | Par point de terminaison |
| Nouvelles tentatives | Aucune, sauf une pour l’échange de configuration d’appel téléphonique | 8 tentatives sur 24 h |
| Enveloppe | type + data | type + data + event_id |
| Rotation du secret | Remplace le secret unique | Secret par point de terminaison |
| Désactiver sans supprimer | PUT /v1/webhook avec {"url": ""} | status=disabled |
| Visibilité du statut | — | active / disabled / failing |
| Échange de configuration bloquant | Oui (telephony.incoming / web.incoming) | Jamais — notifications uniquement |
| Idéal pour | Configuration dynamique des appels | Consommation d’événements en production |
Les nouvelles intégrations doivent consommer les événements via des webhooks basés sur des points de terminaison. Conservez (ou ajoutez) une URL historique uniquement si vous configurez les appels dynamiquement au moment de la prise d’appel ou utilisez la répartition d’outils en mode webhook — ces échanges requête/réponse ne fonctionnent que sur le chemin historique.
Associé
Tous les types d’événements et leurs payloads.
Gérez plusieurs points de terminaison, filtres d’événements et secrets.
La requête bloquante à laquelle votre serveur doit répondre pour configurer les appels.
Payload post-appel avec transcription, enregistrement et métriques.