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é de t, d’un point et du corps brut de la requête.

Vérifier

  1. 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.
  2. Calculez la signature avec votre secret et comparez-la à v1 en temps constant.
  3. Refusez un t de plus de 5 minutes. Chaque nouvel essai porte un t ré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.