Suivre le churn avec les webhooks Polar
Les événements webhook Polar qui signalent résiliation, révocation, paiement échoué ou récupération, livraison et signatures, et un journal du churn.
Polar affiche déjà le churn dans son dashboard. Mais si vous voulez le churn à côté de vos propres données produit (usage, offre, source d’inscription), il vous faut les événements bruts dans votre propre base. Les webhooks de Polar vous donnent tout, à condition de les lire comme Polar le prévoit.
Ce guide fait correspondre les événements d’abonnement de Polar au churn, explique les règles de livraison et de signature qui piègent les handlers, et montre comment construire un journal du churn fiable. Il s’en tient à la documentation de Polar : événements webhook, gérer et surveiller les livraisons, Managing subscriptions, Recovering failed payments, Analytics et l’API Get Metrics (tous consultés en novembre 2026).
Les événements d’abonnement
La page des événements webhook de Polar décrit ces événements d’abonnement :
| Événement | Ce qu’il signifie selon Polar | Sens pour le churn |
|---|---|---|
subscription.created |
Un nouvel abonnement | Client nouveau ou de retour |
subscription.active |
Un abonnement devient actif : nouvel abonnement payant, ou paiement récupéré | Paie |
subscription.updated |
Tout changement sur l’abonnement, y compris les renouvellements | À utiliser pour synchroniser l’état |
subscription.canceled |
Résilié ; le client peut garder l’accès jusqu’à la fin de la période | Churn planifié |
subscription.uncanceled |
Le client a annulé une résiliation en attente | Churn planifié annulé |
subscription.past_due |
Un paiement de renouvellement a échoué et peut encore être récupéré | Risque de churn involontaire |
subscription.revoked |
L’accès prend fin maintenant : la période d’un abonnement résilié s’est terminée, le marchand l’a révoqué, ou les tentatives de paiement sont épuisées | Churn effectif |
Polar précise que subscription.updated est envoyé à chaque changement et suggère d’écouter les événements spécifiques quand seules certaines transitions vous intéressent. Pour le churn, les événements spécifiques sont plus clairs.
L’ordre des événements
Polar documente les séquences, et elles comptent pour le décompte :
- Résiliation en fin de période :
subscription.updatedetsubscription.canceledarrivent immédiatement. À la fin de la période,subscription.updatedetsubscription.revokedsuivent. - Révocation immédiate :
subscription.updated,subscription.canceledetsubscription.revokedarrivent ensemble.
Chaque abonnement churné produit donc à la fois un subscription.canceled et un subscription.revoked, parfois à un mois ou un an d’écart. Comptez le churn sur subscription.revoked. Comptez à part subscription.canceled comme « résiliations planifiées », un indicateur avancé.
Volontaire ou involontaire
subscription.revoked couvre à la fois le churn volontaire (une résiliation est arrivée à terme) et le churn involontaire (les tentatives de paiement sont épuisées). Pour les distinguer :
- Si l’abonnement avait
cancel_at_period_endactivé et unsubscription.canceledsanssubscription.uncanceledensuite, c’est volontaire. - S’il est passé par
subscription.past_duesans revenir àsubscription.active, c’est involontaire.
La page de Polar sur les paiements échoués explique le chemin involontaire : après un renouvellement échoué, l’abonnement passe en past_due et Polar envoie au client un email avec un lien vers le Customer Portal. Il retente 4 fois, à 2, 5, 7 et 7 jours après la tentative précédente (2, 7, 14 et 21 jours après le premier échec), et révoque si toutes les tentatives échouent ou si le code de refus montre que le moyen de paiement ne passera jamais, comme lost_card. Une période de grâce pour les avantages peut être définie (immédiate, 2, 7, 14 ou 21 jours à partir de past_due_at) ; elle ne change pas le calendrier des tentatives.
Une incohérence à connaître : la documentation de Polar sur la révocation et sa référence des webhooks décrivent différemment le statut final après épuisement des tentatives (canceled dans l’une, unpaid dans l’autre). Ne codez ni l’un ni l’autre en dur ; appuyez-vous sur l’événement subscription.revoked et sur votre journal de ce qui a précédé.
Les raisons viennent avec l’abonnement
Quand une résiliation a une raison, elle est sur l’abonnement : customer_cancellation_reason (valeurs too_expensive, missing_features, switched_service, unused, customer_service, low_quality, too_complex, other) et customer_cancellation_comment (Managing subscriptions). Stockez-les depuis le payload de subscription.canceled pour pouvoir découper le churn par raison.
Les règles de livraison
Selon la page sur la livraison de Polar :
- Nouvelles tentatives : les livraisons échouées sont retentées jusqu’à 10 fois avec un backoff exponentiel.
- Délai : 10 secondes, avec la recommandation de répondre en moins de 2 secondes et de faire le travail lourd dans une tâche de fond, car le délai pourrait être réduit.
- Endpoints désactivés : après 10 réponses non-2xx consécutives, l’endpoint est désactivé et tous les membres de l’organisation reçoivent un email. La réactivation est manuelle, dans les réglages des webhooks.
- Pare-feu : le Bot Fight Mode de Cloudflare peut provoquer des échecs 403 ; autorisez les requêtes de Polar avec une règle WAF.
C’est le troisième point qu’il faut anticiper. Un déploiement qui casse votre handler pendant un après-midi peut désactiver l’endpoint, et plus rien ne vous parvient tant que quelqu’un ne le réactive pas.
Les signatures
La même page explique que le schéma de signature dépend de la date de création du secret de l’endpoint. Les secrets créés à partir du 8 septembre 2026 (00:00 UTC) utilisent le schéma Standard Webhooks et se passent tels quels à une bibliothèque Standard Webhooks. Les secrets plus anciens utilisent le HMAC d’origine de Polar, où la chaîne complète du secret whsec_ est encodée en base64 avant d’être donnée à la bibliothèque. Le champ uses_standard_webhook_signature de l’endpoint vous dit lequel s’applique, et les SDK de Polar à partir de 1.0.0-alpha.19 essaient les deux.
En cas de doute, vérifiez ce champ avant de passer des heures à déboguer un « signature mismatch ».
Tester en local
La commande polar webhook listen de la CLI Polar redirige les événements de votre organisation vers une URL locale et affiche le secret à utiliser pour la vérification (CLI webhooks).
Construire un journal du churn
- Un endpoint, des événements précis. Abonnez-vous aux sept événements du tableau ci-dessus.
- Vérifiez, stockez, répondez. Vérifiez la signature, enregistrez le payload brut avec le type d’événement, l’ID d’abonnement, l’ID client et l’horodatage, et renvoyez un 2xx en une ou deux secondes.
- Dédoublonnez. Les nouvelles tentatives entraînent des répétitions. Si vos livraisons portent un ID de message unique, utilisez-le ; sinon, construisez une clé à partir du type d’événement, de l’ID d’abonnement et de l’horodatage de dernière modification de l’abonnement, et ignorez tout ce que vous avez déjà stocké.
- Traitez en tâche de fond. Mettez à jour vos fiches clients à partir de l’événement stocké, jamais dans la requête.
- N’ajoutez que des lignes. Calculez le statut actuel et les indicateurs à partir du journal, pour pouvoir rejouer après un bug.
- Surveillez l’endpoint. Déclenchez une alerte sur les réponses non-2xx avant d’atteindre la limite de 10 échecs de Polar.
- Rapprochez. Chaque semaine, listez les abonnements via l’API et comparez avec votre journal.
Comparer avec les chiffres de Polar
Les Analytics de Polar définissent précisément leurs indicateurs, ce qui en fait un bon point de contrôle :
- Canceled subscriptions : les résiliations déclenchées par les clients sur l’intervalle ; l’abonnement peut encore être actif.
- Churned subscriptions : les abonnements dont la période payée s’est terminée sur l’intervalle.
- Churn rate : les abonnements churnés sur l’intervalle divisés par les abonnements actifs au début de l’intervalle.
- MRR : exclut les abonnements en essai ; inclut ceux en pause et en impayé jusqu’à leur fin effective.
L’API Get Metrics renvoie des champs comme active_subscriptions, churned_subscriptions, canceled_subscriptions, churn_rate et monthly_recurring_revenue par intervalle, avec une répartition des raisons de résiliation.
Si vous adoptez les mêmes définitions dans votre journal, votre taux de churn et celui de Polar doivent concorder sur la même période. Quand ce n’est pas le cas, vous avez probablement manqué des événements, souvent parce que l’endpoint a été désactivé un moment.
Ce que votre propre journal apporte
Les analytics de Polar répondent à « combien de churn ». Votre journal, croisé avec vos données produit, répond à « le churn de qui, et pourquoi » : churn par source d’inscription, par activation, par offre, par raison de résiliation, volontaire ou involontaire. C’est là que se trouve le travail utile.
Le faire avec NoChurn
Polar reste la source de vérité pour les abonnements, les paiements et son calendrier de tentatives, et ses analytics restent utiles. NoChurn s’abonne à vos webhooks Polar, stocke et dédoublonne les événements, sépare le churn volontaire de l’involontaire, et les croise avec les données perso que vous envoyez par API. Il ajoute un churn score de 0 à 100 par client et les raisons recueillies dans son parcours d’annulation. Les litiges restent chez Polar, en tant que marchand officiel, et sont affichés en lecture seule. Voir NoChurn + Polar.