Suivre le churn avec les webhooks Lemon Squeezy
Quels webhooks Lemon Squeezy marquent une annulation, une expiration, un paiement échoué ou récupéré, et comment en faire un journal de churn fiable.
Si vous vendez des abonnements via Lemon Squeezy, tous les signaux de churn dont vous avez besoin passent par ses webhooks : annulations, expirations, renouvellements échoués, récupérations, pauses. Le plus difficile n’est pas de les recevoir. C’est de décider quel événement veut dire quoi, et de les enregistrer de façon à ce que vos chiffres de churn tiennent toujours debout six mois plus tard.
Ce guide fait correspondre les événements d’abonnement de Lemon Squeezy aux notions de churn, montre comment les stocker, et liste les détails de livraison qui cassent les handlers naïfs. Il s’en tient à la documentation de Lemon Squeezy : les types d’événements webhook, la page des requêtes webhook, la signature des requêtes, le guide de synchronisation par webhooks et la référence de l’objet Subscription (tous consultés en novembre 2026).
Les événements d’abonnement
La page des types d’événements de Lemon Squeezy liste ces événements d’abonnement :
| Événement | Ce qui le déclenche selon Lemon Squeezy | Signification pour le churn |
|---|---|---|
subscription_created |
Un nouvel abonnement | Client nouveau (ou de retour) |
subscription_updated |
Tout changement après le paiement initial | À utiliser pour synchroniser le statut |
subscription_cancelled |
Annulé par le client ou le propriétaire de la boutique ; entre en période de grâce jusqu’à la prochaine date de facturation | Churn programmé, pas encore arrivé |
subscription_resumed |
Réactivé après une annulation | Churn programmé annulé |
subscription_expired |
Terminé après une annulation, ou une fois les relances terminées pour les abonnements past_due |
Churn arrivé |
subscription_paused |
Encaissement mis en pause | Pas du churn ; à suivre à part |
subscription_unpaused |
Encaissement repris | Fin de pause |
subscription_payment_success |
Un paiement réussi | Revenu |
subscription_payment_failed |
Un paiement de renouvellement a échoué | Risque de churn involontaire |
subscription_payment_recovered |
Un paiement réussi après un échec | Risque levé |
subscription_payment_refunded |
Un paiement remboursé | Ajustement de revenu |
Deux d’entre eux méritent qu’on s’y attarde.
Annulé ne veut pas dire parti
subscription_cancelled se déclenche quand le client clique sur annuler, mais l’abonnement continue jusqu’à ends_at. Pendant cette période de grâce, il peut être réactivé, ce qui envoie subscription_resumed. Si vous comptez le churn sur subscription_cancelled, chaque abonnement réactivé devient un faux churn, et votre date de churn est décalée de jusqu’à une période de facturation entière.
Comptez le churn sur subscription_expired. Gardez subscription_cancelled comme un indicateur avancé à part : « annulations programmées ce mois-ci ».
Un événement, deux types de churn
subscription_expired se déclenche pour deux raisons très différentes : un client qui a annulé et atteint ends_at (churn volontaire), et un abonnement passé par les relances après des paiements échoués sans être récupéré (churn involontaire). Le nom de l’événement ne vous dit pas lequel.
Pour les séparer, regardez ce qui est arrivé avant. Si votre journal contient un subscription_cancelled pour cet abonnement sans subscription_resumed ensuite, c’est volontaire. S’il contient des événements subscription_payment_failed sans récupération, c’est involontaire. Vous pouvez aussi lire l’état de l’abonnement à la réception de l’événement : la référence de l’objet Subscription montre un booléen cancelled à côté de status.
Le churn volontaire et le churn involontaire demandent des remèdes différents : cette séparation est la chose la plus utile que votre journal puisse vous donner.
À quoi ressemblent les requêtes
D’après la page des requêtes webhook :
- Chaque requête est un
POSTavec un corps JSON:API : la ressource liée à l’événement (un abonnement pour la plupart des événements d’abonnement, une subscription invoice pour les événements de paiement). - L’en-tête
X-Event-Nameporte le nom de l’événement. - L’en-tête
X-Signatureporte la signature. - Votre endpoint doit renvoyer
200. Tout autre statut est retenté jusqu’à trois fois de plus avec un délai exponentiel (la doc donne 5, 25 et 125 secondes en exemple), après quoi la requête est considérée comme échouée.
La page des réglages webhooks de votre dashboard journalise les livraisons récentes avec leur contenu et permet de les renvoyer. C’est votre filet de sécurité si votre endpoint était en panne.
Vérifier la signature
La signature des requêtes explique le principe : vous définissez un secret de signature à la création du webhook (6 à 40 caractères), et Lemon Squeezy calcule avec lui un condensé hexadécimal HMAC-SHA256 du corps de la requête. Recalculez le condensé sur le corps brut, pas sur du JSON resérialisé, et comparez avec une fonction à temps constant. Rejetez tout ce qui ne correspond pas.
Les événements de paiement portent des factures
Les événements subscription_payment_* portent un objet subscription invoice, pas l’abonnement. Il fait référence à l’abonnement : stockez cet identifiant et, si vous avez besoin du statut, récupérez l’abonnement.
Construire un journal de churn pas à pas
L’objectif est une table en ajout seul de ce qui est arrivé à chaque abonnement, à partir de laquelle chaque indicateur de churn peut être recalculé.
- Créez un seul endpoint pour tous les événements d’abonnement. Abonnez-le aux événements du tableau ci-dessus. Le guide de synchronisation de Lemon Squeezy indique que
subscription_updatedcouvre chaque changement : vous pouvez donc vous abonner à lui seul si vous ne voulez que l’état. Pour le suivi du churn, les événements spécifiques sont plus faciles à interpréter : prenez les deux. - Vérifiez, puis stockez le contenu brut. Écrivez le nom de l’événement, l’identifiant de l’abonnement, l’identifiant du client, l’horodatage et le JSON brut avant de faire quoi que ce soit d’autre.
- Dédoublonnez. Les nouvelles tentatives et les renvois manuels font qu’un même événement peut arriver deux fois. Les docs que nous avons utilisées ne décrivent pas d’identifiant d’événement unique dans la requête : construisez votre propre clé à partir du nom de l’événement, de l’identifiant de la ressource et du
updated_atde la ressource, et ignorez les répétitions. - Répondez vite. Renvoyez
200dès que l’événement est stocké, et traitez-le dans une tâche en arrière-plan. Un handler lent qui dépasse le délai déclenche des nouvelles tentatives. - Dérivez, n’écrasez pas. Gardez le journal immuable. Calculez le « statut actuel » et les comptes de churn à partir de lui : un bug dans votre logique peut alors être corrigé en rejouant le journal.
- Rapprochez de temps en temps. Une fois par semaine, listez les abonnements via l’API et comparez leurs statuts à ce que dit votre journal. Tout écart signale un événement manqué.
Les indicateurs que vous pouvez calculer
Avec ce journal, ceux-ci sont simples :
- Taux de churn clients sur un mois : abonnements expirés pendant le mois, divisés par les abonnements actifs au début du mois.
- Part volontaire et involontaire : expirations réparties selon la règle ci-dessus.
- Annulations programmées : événements
subscription_cancelleddu mois, un indicateur avancé du churn du mois suivant. - Sauvetages pendant la période de grâce : abonnements annulés suivis d’un
subscription_resumed. - Taux de récupération des paiements : abonnements avec un
subscription_payment_recovereddivisés par les abonnements avec unsubscription_payment_failedsur la même période. - Part en pause : abonnements actuellement
paused. Présentez-les à part ; ce ne sont ni des payeurs actifs ni des clients partis.
Définissez chaque indicateur une fois, par écrit, et gardez la définition stable. Un taux de churn n’est utile que comparé à lui-même dans le temps.
Tester avant de s’y fier
Le mode test de Lemon Squeezy peut simuler des événements webhook pour les abonnements. Faites-y passer chaque cycle de vie : création, annulation, réactivation, nouvelle annulation puis expiration ; un paiement échoué qui est récupéré ; un autre qui ne l’est pas. Vérifiez qu’à chaque étape votre journal et vos indicateurs correspondent à ce que vous attendez.
Le faire avec NoChurn
Lemon Squeezy reste la source de vérité pour les abonnements et les paiements ; NoChurn ne les modifie pas. NoChurn s’abonne à vos webhooks Lemon Squeezy, stocke et dédoublonne les événements, et sépare pour vous le churn volontaire du churn involontaire. Il ajoute un churn score de 0 à 100 par client, les données personnalisées que vous envoyez par API, et les réponses du parcours d’annulation qui expliquent pourquoi les gens partent. Les litiges, que Lemon Squeezy gère en tant que marchand officiel, sont affichés en lecture seule. Voir NoChurn + Lemon Squeezy.