Ce qu’il faut retenir
- Recevoir plusieurs fois un événement est un comportement à prévoir dans une intégration asynchrone.
- La déduplication de l’événement et l’idempotence de l’action métier répondent à deux problèmes différents.
- La preuve utile relie réception durable, traitement et effet final. Un statut HTTP réussi seul ne suffit pas.
Un paiement déclenche un webhook. Le service active un abonnement, mais sa réponse se perd. Le fournisseur renvoie l’événement. Si le traitement répète aveuglément l’activation, il peut créer une seconde facture, prolonger deux fois une période ou envoyer plusieurs messages. La fiabilité d’une intégration se juge à ce moment précis : celui où le réseau laisse planer le doute sur ce qui a déjà eu lieu.
La livraison n’est pas une transaction partagée
Un webhook est une requête envoyée par un système lorsqu’un événement survient. L’émetteur et le récepteur n’exécutent généralement pas une transaction unique. Les interruptions, les délais de réponse et les reprises créent des cas ambigus. La documentation Stripe signale les livraisons répétées et l’absence de garantie d’ordre entre événements. Cette caractéristique ne doit pas être transformée en anomalie exceptionnelle.
L’idempotence signifie qu’appliquer plusieurs fois une opération identifiée ne produit pas plusieurs effets métier. Elle ne veut pas dire que toutes les répétitions reçoivent le même temps de réponse, ni qu’aucun code n’est réexécuté. Dans un système d’abonnement, l’objectif peut être « cette facture est enregistrée une seule fois » plutôt que « ce processus démarre une seule fois ».
Une bonne intégration API définit ce contrat pour chaque effet. Le signal de réception est séparé de la réalisation métier, et les erreurs sont visibles. Accuser réception avant d’avoir conservé durablement l’événement risque de le perdre ; attendre que tous les services secondaires aient terminé peut provoquer des retransmissions inutiles.
Une boîte de réception durable, avec une unicité réelle
Le point d’entrée vérifie la signature selon le fournisseur, limite la taille reçue et respecte les exigences concernant le corps brut. Il ne transforme pas un contenu non vérifié en ordre d’exécution. Puis il inscrit l’événement dans une boîte de réception persistante. Une contrainte d’unicité sur une clé composée du fournisseur, du compte concerné et de l’identifiant d’événement évite les collisions entre espaces.
Le test « cet identifiant existe-t-il ? » suivi d’un insert ne suffit pas face à deux requêtes simultanées. Les deux peuvent lire une absence avant d’écrire. Une contrainte atomique fournit la protection ; le code traite le conflit attendu comme une répétition. Les champs retenus doivent permettre le traitement et l’investigation sans conserver inutilement des secrets ou toutes les données personnelles du corps.
Une fois cette réception réellement engagée, le serveur peut répondre avec un succès selon le contrat du fournisseur. Un travailleur prend ensuite l’événement et conserve un état de traitement. Les erreurs transitoires autorisent une nouvelle tentative avec attente ; les erreurs de données ou de logique passent dans une file d’exception. Aucune de ces files ne doit devenir un trou noir sans propriétaire.
Le même événement et la même action ne sont pas la même clé
Deux identifiants d’événement différents peuvent décrire une même transition métier. À l’inverse, plusieurs mises à jour valides peuvent concerner le même objet. Dédupliquer uniquement par identifiant d’événement ne protège donc pas nécessairement la création d’une facture. Dédupliquer uniquement par identifiant d’abonnement pourrait supprimer des changements légitimes. Il faut définir la clé qui représente précisément l’opération irréversible.
Exemple hypothétique : un SaaS reçoit deux notifications pour la facture fac_42. Le modèle autorise une seule inscription de cette facture dans le registre local. La clé métier associe le compte du fournisseur et la facture. Un événement sur le remboursement de cette même facture suit une autre opération, avec une autre règle. Une machine à états refuse les transitions incohérentes au lieu de faire confiance au dernier message reçu.
Le problème apparaît aussi dans l’automatisation marketing : deux déclenchements ne doivent pas envoyer deux fois une même séquence d’accueil, mais ils ne doivent pas supprimer un nouveau consentement valide. Le produit doit choisir l’unité de décision avant que le développeur ne choisisse une clé de déduplication.
Relier la donnée locale et l’effet externe
Lorsque l’effet reste dans la même base, une transaction peut enregistrer le résultat métier et la fin du traitement ensemble. Dès qu’un appel externe intervient, cette atomicité disparaît. Écrire « traité » puis envoyer un courriel peut perdre le message après un crash. Envoyer le courriel puis écrire « traité » peut le dupliquer après un crash. Une boîte d’envoi transactionnelle, ou outbox, conserve la demande d’effet dans la transaction locale.
Un processus séparé exécute les demandes de cette boîte. Il utilise une clé d’idempotence lorsque le destinataire en propose une, puis conserve un identifiant de réponse durable. Stripe documente ce contrat pour ses requêtes idempotentes. La durée de conservation et les règles du fournisseur restent à lire : une clé oubliée après son expiration ne protège pas une reprise très tardive.
Un service externe sans idempotence impose d’autres mesures : recherche d’un résultat existant, limitation des effets ou validation humaine avant réessai lorsque l’état est ambigu. Affirmer « exactement une fois » pour toute la chaîne masquerait cette frontière. L’objectif opérationnel est un effet métier unique vérifiable, avec une méthode de résolution des situations incertaines.
Trois preuves pour un événement reçu
| Étape | Preuve | Piège évité |
|---|---|---|
| Réception | Identifiant unique engagé en stockage | Succès HTTP avant persistance |
| Décision métier | Transition autorisée et clé d’opération | Doublon distinct sur le même effet |
| Effet externe | Identifiant du destinataire et résultat relu | Nouvelle tentative après réponse perdue |
Ne pas confondre ordre d’arrivée et état actuel
Un événement ancien peut arriver après un récent. Le service peut relire l’objet auprès de la source lorsque cette stratégie est compatible avec ses quotas et sa disponibilité. Il peut aussi appliquer une version monotone ou une transition métier explicite. Trier les messages par heure locale de réception ne reconstitue pas l’ordre métier ; une horloge ou une file ne crée pas une garantie que le fournisseur n’offre pas.
Une réconciliation périodique compare les objets locaux et externes sur une période définie. Elle détecte les événements perdus, les états en attente et les effets sans confirmation. Le résultat est une liste d’écarts traitable, pas seulement un compteur. Pour une automatisation pilotée par un agent, cette étape est particulièrement importante : une décision autonome doit s’appuyer sur un état relu et non sur la dernière notification aperçue.
La recette doit créer des ambiguïtés
Rejouer le même événement, l’envoyer simultanément deux fois, inverser deux événements et interrompre le travailleur après l’effet externe sont des tests utiles. Une réponse perdue, une signature invalide et un changement d’objet pendant le traitement complètent la grille. À chaque fois, vérifier le nombre d’effets réellement produits, pas seulement l’absence d’erreur dans le log.
La supervision mesure l’âge du plus ancien événement en attente, le nombre de reprises et les exceptions non résolues. Les délais attendus sont définis par opération. La prochaine étape consiste à choisir un webhook critique et à démontrer, sous concurrence et interruption, que son effet reste unique ou qu’une ambiguïté déclenche une réconciliation explicite.
Sources et références
Mise à jour du 1er octobre 2026 : références vérifiées à cette date. Les mécanismes décrits sont durables ; les options exactes restent à vérifier dans la version du produit utilisée.
- Stripe — Webhooks — doublons, ordre et vérification des signatures
- Stripe — Idempotent requests — contrat des clés d’idempotence

