---
title: "Des webhooks qui arrivent, et que faire quand ils arrivent deux fois"
summary: "Douze événements, une signature sur l'horodatage et le corps, huit tentatives en 21 heures environ, et un id par événement pour écarter les doublons sans peine."
author: "Samuel Krauss"
author_title: "Founder"
publisher: "Elchi Studios"
published: 2026-10-03
updated: 2026-10-09
url: https://elchi.dev/fr/journal/des-webhooks-qui-arrivent
language: fr
translation_of: https://elchi.dev/en/journal/webhooks-that-arrive
tags: ["eauth","webhooks","integration"]
words: 1203
---

# Des webhooks qui arrivent, et que faire quand ils arrivent deux fois

*Par Samuel Krauss, Founder. Publié le 3 octobre 2026.*

> Douze événements, une signature sur l'horodatage et le corps, huit tentatives en 21 heures environ, et un id par événement pour écarter les doublons sans peine.

Un fournisseur de connexion qui se contente de répondre aux questions n'est qu'à moitié un fournisseur. Votre système doit savoir quand une personne s'inscrit, quand elle se connecte, quand elle révoque l'accès de votre application ou quand son compte est supprimé, sans avoir à le demander en boucle. EAuth vous le dit par des webhooks, et cet article porte sur ce qui décide si une intégration de webhooks tient en production : ce qui est envoyé, comment vous savez que cela vient de nous, ce qui se passe quand votre serveur est hors service, et que faire d'une livraison déjà reçue.

## Ce qui est envoyé

Douze événements. Sept concernent les personnes et leurs sessions : `user.created` quand quelqu'un s'inscrit ou que le single sign-on crée un compte ; `user.signed_in` à chaque code d'autorisation que vous échangez, pas lors des refresh ; `consent.granted` et `consent.revoked` ; `session.revoked` avec un motif, chaque fois que vos refresh tokens pour quelqu'un ont cessé de fonctionner sans que vous l'ayez demandé, ce qui couvre la déconnexion partout, une réinitialisation de mot de passe, un token utilisé deux fois et une personne qui quitte une organisation ; `user.password_changed` ; et `user.deleted`. Cinq concernent les organisations : création, suppression, et un membre ajouté, modifié ou retiré. Un endpoint abonné à rien les reçoit tous.

Chaque corps est un petit objet JSON : l'id et le type de l'événement, le moment où il s'est produit, votre client id, et un objet `data` avec des id et guère plus. Si vous avez besoin du nom ou de l'adresse actuels de la personne, vous les demandez à l'API avec l'id. Un webhook qui transporterait le profil serait une copie périmée dès l'instant où elle est écrite.

Depuis le 3 octobre, une règle se voit dans l'ordre des événements : aucune application ne reçoit de compte dont l'adresse e-mail n'est pas confirmée. `user.created` arrive toujours au moment où quelqu'un s'inscrit, avec `email_verified` à false, mais votre application ne reçoit pas de token pour cette personne tant qu'elle n'a pas ouvert le lien. `user.signed_in` vient donc après la confirmation, et quelqu'un qui ne confirme jamais ne vous laisse qu'un `user.created`. Un compte créé par single sign-on arrive avec `email_verified` à true.

## Comment vous savez que cela vient de nous

Chaque livraison porte `Elchi-Signature: t=<timestamp>,v1=<hmac>`, où le HMAC-SHA256 est calculé avec le secret de votre endpoint sur l'horodatage, un point et le corps brut. Le schéma est celui de Stripe avec un en-tête renommé : un vérificateur existant n'a besoin que d'une ligne modifiée. Vérifiez la signature sur les octets bruts reçus, avant de parser quoi que ce soit, avec une comparaison en temps constant, et refusez un horodatage qui s'écarte de plus de cinq minutes de votre propre horloge. C'est l'horodatage dans la chaîne signée qui rend inutile une livraison interceptée : le corps d'un événement `user.deleted` ne change jamais, et sans l'horodatage, un rejeu serait impossible à distinguer de l'original.

Le secret commence par `whsec_`, ce qui le rend reconnaissable dans un log ou lors d'un scan de dépôt s'il fuit, et il n'est affiché qu'une fois, à la création de l'endpoint. De notre côté, ce ne peut pas être un condensé, puisqu'il doit signer chaque livraison : il est donc stocké chiffré en AES-256-GCM, sous une clé qui ne touche jamais la base de données. L'export de votre application le laisse de côté. Il n'y a pas de bouton de rotation : pour changer le secret, ajoutez un second endpoint avec la même URL, acceptez l'un ou l'autre secret jusqu'à ce que le nouveau livre, puis supprimez l'ancien. Pendant ce chevauchement, chaque événement arrive deux fois avec le même id, ce que la section d'après la suivante rend sans conséquence.

## Ce qui se passe quand votre serveur est hors service

Une livraison qui n'obtient pas de réponse 2xx dans les dix secondes a échoué. Chaque événement a droit à huit tentatives : la première tout de suite, puis après 30 secondes, 2, 10 et 30 minutes, et 2, 6 et 12 heures, chaque attente comptée à partir de la tentative précédente. La dernière tentative a lieu environ 21 heures après la première, et une livraison qui échoue encore est marquée comme abandonnée.

L'endpoint dans son ensemble a aussi une limite. Dès que 25 tentatives d'affilée ont échoué, tous événements confondus, il est désactivé, et la console le marque comme tel. Aucun mail n'est envoyé. Tant qu'il est désactivé, rien n'est mis en file pour lui : les événements survenus pendant ce temps ne sont jamais livrés. Les livraisons qui attendaient déjà reprennent quand vous réactivez l'endpoint dans la console, et une livraison abandonnée peut être renvoyée à la main.

Mis bout à bout, voilà le compromis. Une application calme dont le récepteur tombe pendant dix minutes ne perd rien : une poignée d'événements, quelques nouvelles tentatives chacun. Une application chargée atteint 25 tentatives échouées en quelques minutes, et à partir de là, jusqu'à ce que quelqu'un réactive l'endpoint, ses événements sont perdus. Après une panne de votre récepteur, regardez la console, pas seulement vos propres logs.

Dix secondes, ce n'est pas long, et c'est voulu. Répondez 200 dès que vous avez stocké le corps, et faites le travail ensuite. Un handler qui appelle trois autres services avant de répondre est retenté comme s'il avait échoué, et il s'exécute alors quatre fois.

## Que faire d'une livraison déjà reçue

Toutes les tentatives d'un événement portent le même `Elchi-Event-Id`, qui est aussi l'`id` du corps. Stockez-le avec le travail effectué, et quand arrive un id que vous avez déjà, répondez 200 et jetez le corps sans le parser. Cette seule règle transforme la livraison au moins une fois, ce que tout système de webhooks offre honnêtement, en traitement exactement une fois de votre côté.

Elle rend aussi le bouton de renvoi sans danger. La console liste les huit livraisons les plus récentes de chaque endpoint avec leur statut, leur code de réponse, leurs tentatives et leur heure, et toute livraison qui n'a pas abouti peut être renvoyée. Les livraisons sont conservées 30 jours, puis supprimées avec leur contenu, puisqu'un contenu nomme une personne.

## Règles pour l'URL

`https` uniquement, avec un hôte et sans identifiants. `localhost` et les adresses littérales privées ou réservées sont refusés à l'enregistrement de l'endpoint. Un nom d'hôte est vérifié au moment où il sert : chaque adresse vers laquelle il pointe est revérifiée à chaque connexion, donc un nom qui pointe plus tard vers l'intérieur d'un réseau est quand même refusé. C'est ce contrôle qui empêche d'utiliser un système de webhooks pour atteindre un serveur qu'il ne devrait jamais voir. Les redirections ne sont pas suivies, car en suivre une pourrait transformer un POST signé en requête vers un endroit que vous n'avez jamais enregistré.

## Ce qui n'existe pas

Il n'y a ni regroupement ni garantie d'ordre entre les événements : deux événements concernant une même personne peuvent arriver dans le désordre quand l'un est retenté, alors traitez chacun selon ses propres faits, `session.revoked` compris. Il n'y a pas d'événement pour un import, puisque votre système connaît déjà ces personnes. Il n'y a pas de file d'attente pour la période où un endpoint est désactivé, ni de mail quand cela arrive. Et la console montre les huit dernières livraisons par endpoint, pas un journal consultable des 30 jours.

## Sources

1. [Webhooks, EAuth documentation](https://docs.elchi.dev/webhooks), Elchi Studios, consulté le 2026-10-03
2. [RFC 2104: HMAC: Keyed-Hashing for Message Authentication](https://www.rfc-editor.org/rfc/rfc2104.html), IETF, consulté le 2026-10-03
3. [Resolve webhook signature verification errors](https://docs.stripe.com/webhooks/signature), Stripe, consulté le 2026-10-03
4. [Server-Side Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html), OWASP, consulté le 2026-10-03
