Aller au contenu

Le catalogue d'events

La liste ci-dessous est l’inventaire réel des events, relevé dans le code iOS et Android.

Parité : sauf mention contraire, chaque event part à l’identique sur iOS et Android. Seuls les events ATT sont propres à iOS (la transparence du suivi est une notion Apple). L’ouverture de l’app est capturée automatiquement par PostHog (event lifecycle Application Opened).

Event Déclencheur Propriétés
onboarding_started L’utilisateur arrive sur le 1er step réel (après le Welcome) — entrée dans le funnel social_signup
onboarding_step_viewed Arrivée sur l’étape signup uniquement (depuis le 5/8/26) step_id, step_index, social_signup
onboarding_step_completed Validation d’un step step_id, step_index, social_signup, value (selon le step)
onboarding_completed Compte créé, profil enregistré auth_method, cigarettes_per_day, quit_timeline, social_signup, total_duration_seconds
welcome_sign_in_tapped Tap « J’ai déjà un compte » sur l’écran Welcome (aucune)
auth_method_tapped Tap sur un bouton de connexion, avant que le flux ne tourne (= dénominateur par méthode) auth_method (apple / google / email)
signup_form_started L’utilisateur met le focus dans le formulaire email/mot de passe (détecte les abandons silencieux) (aucune)
auth_succeeded Le fournisseur a rendu une session valide, avant toute finalisation d’onboarding auth_method, flow (signup / login)
existing_account_found Les identifiants pointent vers un compte qui a déjà terminé l’onboarding : ce n’est pas une inscription mais un retour auth_method
signup_failed Échec d’authentification auth_method, error_type (email_already_used, network, oauth_cancelled, oauth_failed, invalid_email, password_too_short, invalid_credentials, email_confirmation_required, invalid_otp, rate_limited, passwords_mismatch, same_password, account_deletion_failed, unknown)
tracking_empty_state_viewed Affichage de l’état vide de l’onglet Suivi (celui qui propose les quiz aux gens sans date d’arrêt) — haut du funnel quiz quiz_types_shown (["dependency","motivation"])
quiz_started Ouverture d’un quiz — tap sur la carte de l’état vide de Suivi ou tap sur la notification de relance quiz quiz_type, source (tracking_empty_state / quiz_reminder)
onboarding_quiz_completed Quiz motivation/dépendance terminé quiz_type, score, max_score, result_tier, source (tracking_empty_state / quiz_reminder)

L’écran de connexion n’émettait aucun event jusqu’au 28/7/26 : un revenant était invisible entre son arrivée et son prochain lancement à froid, et les échecs de connexion n’étaient jamais comptés.

Event Déclencheur Propriétés
login_method_tapped Tap sur un bouton de l’écran de connexion, avant que le flux ne tourne auth_method (apple / google / email)
auth_succeeded Session obtenue depuis l’écran de connexion auth_method, flow = login
login_failed Échec de connexion auth_method, error_type (même vocabulaire que signup_failed), error_detail (Android, chemin Google uniquement)
Event Déclencheur Propriétés
lesson_started Démarrage d’une leçon key_id, lesson_index, key_is_premium
lesson_completed Vidéo vue à plus de 90 % key_id, lesson_index
key_completed Clé bouclée (toutes les leçons + quiz réussi) — une seule fois par clé, pour de bon key_id
video_play_started Lecture d’une vidéo (leçon, envie ou rechute) qui démarre vraiment video_key, video_kind (lesson / craving / relapse), video_index, locale
video_play_failed Échec de lecture d’une vidéo devenu visible pour l’utilisateur (après les auto-retries) — leçon, envie ou rechute video_kind (lesson / craving / relapse), video_key, video_index, locale, reason, http_status (optionnel), attempts

video_play_failed alimente une alerte PostHog sur les pics d’échec (une panne CDN était jusque-là invisible en analytics), en complément de la sonde serveur /api/health/video. reasonurl_resolve (aucune URL résolue), network, timeout, http_403 (token signé périmé/refusé), http_5xx, http_other, decode, stall (lecture bloquée, irrécupérable), unknown. À noter : ici video_key est la clé nue (key0), alors que video_play_started envoie le mapKey composite (key0-1-fr) — on reconstitue via video_index + locale.

Event Déclencheur Propriétés
craving_help_started Tap sur le bouton d’aide à l’envie (aucune)
craving_trigger_submitted Sélection d’un déclencheur (stress, ennui…) trigger_id, trigger_index
craving_activity_started Début d’une activité (respiration, jeu…) trigger_id, activity_id, activity_category
craving_activity_completed Fin ou abandon de l’activité trigger_id, activity_id, activity_category, duration_seconds, completed (bool)
relapse_video_started Lecture d’une vidéo « rechute » (signal de difficulté) video_index
Event Déclencheur Propriétés
quit_date_updated Date d’arrêt définie ou changée source (tracking / profile), days_from_now (négatif = passé, 0 = aujourd’hui, positif = futur)
planned_quit_date_set Date d’arrêt planifiée (avant d’avoir arrêté) source, days_from_now, reset_streak (optionnel)
health_milestone_reached Seuil de santé franchi milestone_id (1-17), milestone_name, days_since_quit
achievement_unlocked Nouveau succès débloqué — ne part qu’une fois par appareil achievement_id, category (time / health / earnings / cigarettes / activity / programme / pratique), days_since_quit
Event Déclencheur Propriétés
paywall_viewed Le paywall s’affiche source, key_id (optionnel), trigger (optionnel)
paywall_dismissed Le paywall est fermé sans achat source, key_id (opt.), trigger (opt.), view_duration_seconds (opt.)
purchase_sync_result L’app demande au store de resynchroniser les achats source, outcome, error_code + error_name (si outcome = error)
subscription_ending_viewed La carte « Ton accès se termine le… » s’affiche (abonné payant qui a encore l’accès mais ne renouvellera pas) days_left
subscription_ending_tapped Tap sur cette carte, qui ouvre l’écran d’abonnements du store days_left

Les valeurs de source (d’où vient le paywall), celles de trigger (ce qui l’a déclenché, quand la source en porte un : auto / card / notification / moment_notification pour comeback — les deux derniers mergés le 18/8/26, pas encore publiés) et le détail premium sont décrits dans Monétisation. Les events d’achat ne sont pas listés ici : ils arrivent automatiquement de RevenueCat (rc_*).

Récupération d’un achat perdu (purchase_sync_result)

Section intitulée « Récupération d’un achat perdu (purchase_sync_result) »

Un abonnement payé sur le store peut ne jamais arriver jusqu’à RevenueCat (coupure réseau après le paiement, app tuée, store en erreur). L’argent est prélevé, l’utilisateur reste non-premium, et jusqu’ici ça ne laissait aucune trace. L’app demande donc une resynchronisation à trois moments, et chacun envoie cet event :

  • source = signup — à la fin de la création de compte. C’est le cas qui compte : quelqu’un qui a payé sur un ancien compte (ou avant une réinstallation) récupère son abonnement sans rien faire, grâce au transfert RevenueCat.
  • source = relaunch — au démarrage suivant, uniquement si la resynchro de signup a échoué. Trois tentatives au maximum, puis on s’arrête (le bouton manuel reste disponible).
  • source = profile_restore — l’utilisateur a appuyé sur « Restaurer mes achats » dans le Profil.

outcome vaut :

Valeur Sens
recovered Le compte n’était pas premium et l’est devenu — un achat a bien été récupéré
already_premium L’utilisateur était déjà premium, rien n’a changé
nothing_found Le store n’a rien à rattacher à ce compte (cas normal de l’écrasante majorité)
error Le store ou RevenueCat a renvoyé une erreur ; error_code (code RevenueCat numérique, identique iOS/Android) et error_name (libellé, format propre à chaque plateforme) disent laquelle

À surveiller : le volume de outcome = error sur source = signup (chaque occurrence est un utilisateur potentiellement lésé) et le nombre de recovered, qui mesure ce que le rattrapage sauve réellement.

Aucun event. book_tapped a été retiré le 5/8/26 : il n’alimentait aucun insight. À réinstrumenter le jour où les livres reflowables sortent, avec une question précise en tête.

Les notifications sont locales (calculées sur l’appareil à partir de la date d’arrêt), il n’y a pas de serveur de push. Elles sont entièrement mesurées :

Event Déclencheur Propriétés
notification_opened Tap sur une notification (deep-link) category, moment_index ou achievement_id ; pour la préparation : prep_target (setDate / content) + prep_step (1 à 10) ; pour le quiz : quiz_type (dependency / motivation) ; pour le comeback : category = comeback seul (mergé le 18/8/26, pas encore publié)
notification_prompt_shown L’écran de demande de permission s’affiche mode, attempt
notification_prompt_responded Réponse à la demande de permission mode, action
notification_permission_updated La permission change (prompt ou réglages OS) enabled, source (onboarding / onboarding_skip / post_login_priming)

ATT — App Tracking Transparency (iOS uniquement)

Section intitulée « ATT — App Tracking Transparency (iOS uniquement) »
Event Déclencheur Propriétés
att_prompt_shown L’écran d’amorce ATT s’affiche attempt
att_prompt_responded Tap sur le bouton de l’amorce action (continue / later)
att_authorization_changed La boîte de dialogue système ATT est résolue status (authorized / denied / restricted / notDetermined)
Event Déclencheur Propriétés
review_prompt_requested On demande au store d’afficher sa boîte de notation trigger (health_milestone / achievement_unlocked / key_quiz_passed / lesson_completed / moment_read), outcome (requested / no_activity / no_scene), positive_count

Cet event ne mesure que le fait d’avoir demandé — jamais une note. Ni Apple ni Google ne disent si la boîte s’est affichée, encore moins si l’utilisateur a noté : les deux appliquent leur propre quota, silencieusement. Un review_prompt_requested ne veut donc pas dire « boîte vue », et le nombre d’avis ne se rapproche que par les compteurs des stores.

C’est malgré tout ce qui manquait : sans event, le fait que le prompt Android ne partait jamais est resté invisible pendant toute la vie de l’app. Voir Notation.

Event Déclencheur Propriétés
account_deleted Suppression de compte réussie (aucune)

Depuis le 5/8/26, les pages marketing du site (accueil, page de vente /app, conférence) sont mesurées. Avant cette date le site était un angle mort complet : aucune donnée, dans aucun outil.

Ces events se lisent sur le dashboard Site — page de vente (kaiho.fr/app).

Event Déclencheur Propriétés
$pageview Chargement d’une page marketing. Émis à la main, pas par le SDK, pour qu’il porte page et lang page, lang
landing_section_reached Une section jalon entre dans le viewport (au quart visible), une seule fois par visite section (problem, antimethod, keys, program, social_proof, pricing, cta_final), page, lang
landing_store_tapped Clic sur un badge App Store ou Google Play store (apple, google), position (hero, program, cta_final, sticky, fallback), page, lang
landing_faq_opened Ouverture d’une question fréquente question (q1q5), page, lang
landing_store_escape Une étape de la cascade de secours vers l’App Store (voir ci-dessous) step, page, lang

page vaut home, app, conference ou support ; lang est la langue de la page, déduite de l’URL.

Sept sections jalons, pas treize. La page de vente compte treize sections ; seules celles dont l’atteinte change quelque chose sont instrumentées. Le reste serait du volume sans question en face.

Les quatre premières (program, social_proof, pricing, cta_final) datent du 5/8/26 et répondent à « qu’est-ce que la personne a vu ». Trois ont été ajoutées le 11/8/26problem, antimethod, keys — pour répondre à une autre question : où exactement les gens décrochent. La première lecture montrait que seuls 30 % des visiteurs atteignaient program, la 6ᵉ section, sans dire si la chute avait lieu au deuxième écran ou au cinquième. Ces trois jalons couvrent l’intervalle.

La bande presse, qui occupait la section 2, n’a pas de jalon : elle a été remontée dans le hero le 11/8/26 (voir ci-dessous), donc tout le monde la voit et un jalon n’aurait rien mesuré.

À retenir avant de lire ces jalons : 80 % des taps sur les badges stores partent du hero, et 30 % seulement des visiteurs atteignent la 6ᵉ section. La page ne vend pas progressivement, elle convertit en haut ou pas du tout.

Deux conséquences tirées le 11/8/26 :

  • la preuve média (M6, France Inter, France 24, Top Santé, et « coach tabac n°1 élu par Psychologies Magazine ») a quitté la section 2 pour le hero ; le composant PressStrip continue de servir la page d’accueil, où il n’a pas bougé ;
  • la réponse à « combien de temps avant d’arrêter ? » a été ajoutée sous les badges. C’est, à égalité avec « est-ce vraiment gratuit ? », la question la plus ouverte de la FAQ (deux fois devant les trois autres), et elle n’était répondue qu’en bas de page, vue par un visiteur sur dix. La gratuité, elle, était déjà annoncée dans le hero.

Rien n’a été ajouté au-dessus des badges : ils portent l’essentiel des taps, les repousser vers le bas coûterait plus que le gain espéré.

Sur iPhone, un lien apps.apple.com ne sert jamais une page : Apple répond une redirection vers son schéma maison itms-appss://. Safari sait passer la main à l’App Store, mais le navigateur intégré d’Instagram ou de TikTok, lui, abandonne — et le tap ne fait rien. Le site tente donc trois choses dans l’ordre : le schéma en direct, puis une demande d’ouverture dans Safari, puis un panneau qui explique comment sortir à la main.

Cette cascade a été instrumentée le 11/8/26, parce qu’elle est le seul mécanisme posé sur le chemin du téléchargement et qu’on était incapable de dire si elle rattrapait les gens ou si elle les perdait.

step Signification
cascade_started Badge App Store tapé depuis un navigateur intégré : la cascade démarre
safari_handoff Le schéma direct n’a pas passé la main, on tente l’ouverture dans Safari
panel_shown Les deux tentatives ont échoué : le pas à pas manuel s’affiche
resume_started Page rouverte dans un vrai navigateur, reprise automatique du voyage
resume_panel_shown iOS a refusé le départ automatique : on redemande le geste manquant
panel_copy_tapped Adresse de l’App Store copiée depuis le panneau

On ne compte que les échecs, jamais les réussites — et c’est voulu. Une réussite fait disparaître la page, donc le minuteur qui la constaterait ne tourne qu’au retour de la personne, et jamais si elle ne revient pas : compter les réussites les sous-estimerait. Chaque étape ci-dessus se produit au contraire pendant que la page est vivante. Les réussites se déduisent :

  • ouvert par le schéma direct = cascade_startedsafari_handoff
  • ouvert via Safari = safari_handoffpanel_shown

À noter : panel_shown arrive après un rechargement de la page (la destination est inscrite dans l’adresse avant d’afficher le panneau), donc il n’est pas dans la même vue de page que le cascade_started qui l’a provoqué. Les deux restent rattachés à la même personne.

Depuis le 11/8/26, la page n’affiche qu’un badge : celui qui correspond au téléphone du visiteur. L’autre magasin passe en lien texte discret, sous le badge.

Le motif est mesuré, pas esthétique : sur les six premiers jours, 14 % des personnes qui tapaient un badge tapaient celui du mauvais magasin — 18 % des tapeurs Android partaient vers l’App Store, 8 % des tapeurs iOS vers Google Play. Le badge Apple était rendu en premier et ramassait les taps distraits. Ces taps-là ne mènent nulle part.

L’effet se relit sur landing_store_tapped en croisant store avec $os : la part de taps « croisés » doit tomber.

Le site n’écrit rien dans le navigateur : ni cookie, ni stockage local, ni stockage de session. PostHog tourne en cookieless_mode, l’identité étant un hash irréversible calculé côté serveur à partir d’un sel quotidien ensuite détruit. C’est ce qui permet de se passer de bandeau de consentement, et c’est déclaré dans la politique de confidentialité.

Trois conséquences à connaître avant de lire ces chiffres :

  • Pas de pays. En mode cookieless l’IP est retirée avant l’enrichissement : ni géolocalisation, ni détection de robot sur ces events. La carte du monde de Web Analytics restera vide. C’est lang qui joue le rôle de segment.
  • Les visiteurs uniques ne valent que sur 24 h. Le sel change chaque jour, donc la même personne compte comme une nouvelle personne le lendemain : tout « unique » hebdomadaire ou mensuel est surévalué. Les tuiles comptent volontairement des events, pas des personnes.
  • Aucun lien avec l’app. Un clic sur un badge ne se rattache à aucune installation ni à aucune inscription. Faire ce lien demanderait des paramètres d’attribution sur les liens stores, ce qui a été écarté : les liens partent nus vers /app.

Les pages légales et d’assistance ne sont pas mesurées du tout, et le réglage « Do Not Track » du navigateur est respecté. Seule la production compte : les déploiements de prévisualisation Cloudflare et le dev local n’envoient rien.

Aucun event. Le lien de téléchargement /dl (voir Lien de téléchargement) envoyait smartlink_clickedretiré le 5/8/26, il n’alimentait aucun insight. Conséquence assumée : on n’a plus de mesure des clics sur /dl. Pour le volume, il reste les analytics Cloudflare Pages.