Votre serveur
Vérifier la signature d’un webhook
Chaque appel du webhook est signé avec votre secret : vérifiez la signature avant de traiter l’événement.
Mis à jour le 28 octobre 2026
Chaque appel porte l’en-tête Zean-Signature, de la forme t=1760515200,v1=5f2b…. Vérifiez-le pour être sûr que l’appel vient bien de Zean et n’a pas été modifié.
t: le moment de l’envoi, en secondes Unix.v1: la signature HMAC-SHA256 en hexadécimal, calculée avec le secret de signature sur le texte formé det, d’un point et du corps brut de la requête.
Vérifier
- Lisez le corps brut de la requête, avant tout décodage JSON : un JSON décodé puis réécrit ne donne plus la même signature.
- Calculez la signature avec votre secret et comparez-la à
v1en temps constant. - Refusez un
tde plus de 5 minutes. Chaque nouvel essai porte untrécent.
Fichier : zean-signature.js
import crypto from 'node:crypto';// Vrai quand l’appel est signé avec votre secret et envoyé il y a moins de cinq minutes.export function isValidSignature(rawBody, header, secret) { const fields = Object.fromEntries(String(header).split(',').map(field => field.split('='))); const fresh = Math.abs(Date.now() / 1000 - Number(fields.t)) < 300; const expected = crypto.createHmac('sha256', secret).update(`${fields.t}.${rawBody}`).digest(); const received = Buffer.from(fields.v1 ?? '', 'hex'); return fresh && received.length === expected.length && crypto.timingSafeEqual(received, expected);}Dans votre route
import express from 'express';import { isValidSignature } from './zean-signature.js';const app = express();// Le corps brut : la signature porte sur les octets envoyés, avant tout décodage JSON.app.post('/zean/webhook', express.raw({ type: 'application/json' }), (req, res) => { if (!isValidSignature(req.body, req.get('zean-signature'), process.env.ZEAN_WEBHOOK_SECRET)) { return res.sendStatus(401); } const event = JSON.parse(req.body); // Répondez en moins de 10 secondes, puis faites le travail long. res.sendStatus(200); console.log(event.type, event.data.conversationId);});En développement
Les appels que la CLI Zean transmet à votre ordinateur sont signés de la même façon, avec le secret d’écoute à la place du secret de signature. Le code ne change pas : mettez le secret d’écoute dans ZEAN_WEBHOOK_SECRET en développement, et le secret de signature en production. Voir Recevoir les webhooks sur votre ordinateur.
Le secret de signature est dans Paramètres de l’App > Développeurs > Webhook. Après l’avoir régénéré, mettez à jour votre serveur tout de suite : les appels suivants, nouveaux essais compris, sont signés avec le nouveau secret.