Webhooks et vérification de signature
Événements, charge utile, relances, vérification de la signature en Node.js et en Python, et rotation du secret sans coupure.
Configurer un point de terminaison
Dans le dashboard, ouvrez Développeurs, puis Webhooks, ajoutez votre URL et choisissez les événements. La page affiche le secret qui sert à signer les livraisons. INFYNIA appelle votre URL avec une requête POST et un corps JSON.
Événements
Dix-neuf événements existent : call.started, call.ended, call.failed, call.transcript_ready, call.recording_ready, call.analysis_ready, call.custom_event, agent.created, agent.updated, agent.status_changed, agent.alert, contact.created, contact.updated, contact.deleted, knowledge.document_ready, knowledge.document_failed, campaign.started, campaign.completed et campaign.cancelled. Le schéma de chacun se trouve dans la section webhooks de la référence.
{
"id": "evt_call_123_call.ended",
"eventType": "call.ended",
"timestamp": "2026-01-15T10:30:00.000Z",
"organizationId": "org_123",
"version": "1.0",
"livemode": true,
"data": { "callId": "call_123", "agentId": "agent_123" }
}idest unique par événement et identique à chaque relance : servez-vous-en pour dédupliquer.livemodevautfalsepour l'activité des clés de test et pour les événements de test.- Les livraisons sont garanties au moins une fois : un même événement peut arriver deux fois.
Répondre et relances
- Répondez avec n'importe quel statut
2xxdès que vous avez enregistré l'événement. Toute autre réponse, un dépassement de délai ou une erreur réseau compte comme un échec. - Une livraison en échec est relancée jusqu'à 6 fois : après 1 minute, 5 minutes, 30 minutes, 2 heures, 6 heures puis 12 heures (7 tentatives au total).
- Après 20 tentatives échouées consécutives, le webhook est désactivé automatiquement et les propriétaires et administrateurs sont prévenus par e-mail. Réactivez-le dans le dashboard une fois votre point de terminaison réparé.
- Vous pouvez rejouer une livraison depuis le journal des livraisons du dashboard.
Vérifier la signature
Chaque livraison porte une signature dans l'en-tête X-Webhook-Signature : le HMAC-SHA256, en hexadécimal, du corps brut de la requête, calculé avec votre secret. Vérifiez-la toujours, avec le corps exactement tel que reçu (avant tout décodage JSON), et comparez en temps constant.
# Normal case
X-Webhook-Signature: sha256=<hex>
# During a secret rotation (24 hours): new secret first, then the old one
X-Webhook-Signature: v1=<hex signed with the new secret>,v1=<hex signed with the old secret>Pendant une rotation de secret, l'en-tête contient deux signatures séparées par une virgule. Acceptez la livraison quand l'une d'elles correspond à l'un de vos secrets.
import crypto from "node:crypto";
import express from "express";
// During a rotation, put the new secret first and the old one second.
const SECRETS = [process.env.INFYNIA_WEBHOOK_SECRET, process.env.INFYNIA_WEBHOOK_SECRET_PREVIOUS].filter(Boolean);
function isValid(rawBody, header) {
const received = (header ?? "")
.split(",")
.map((part) => part.trim().replace(/^(sha256|v1)=/, ""))
.filter(Boolean);
return SECRETS.some((secret) => {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return received.some(
(sig) =>
sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)),
);
});
}
const app = express();
// The signature covers the raw body: do not parse it as JSON before verifying.
app.post("/infynia", express.raw({ type: "application/json" }), (req, res) => {
if (!isValid(req.body, req.get("x-webhook-signature"))) return res.sendStatus(401);
const event = JSON.parse(req.body.toString("utf8"));
// Answer fast, process after. Deduplicate on event.id.
res.sendStatus(200);
});La signature n'inclut pas d'horodatage. Pour vous protéger des rejeux, conservez l'id des événements traités et ignorez les doublons.
Renouveler le secret sans coupure
- Dans
Développeurs,Webhooks, renouvelez le secret. Le nouveau secret n'est affiché qu'une fois. - Pendant 24 heures, les livraisons portent les deux signatures :
v1=<nouveau>,v1=<ancien>. Votre récepteur, écrit comme ci-dessus, accepte les deux tant que vous configurez les deux secrets. - Déployez le nouveau secret dans votre récepteur, puis retirez l'ancien après les 24 heures.
Un webhook sans rotation en cours garde le format simple sha256=<hex> : les récepteurs existants continuent de fonctionner.