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).
Onboarding & activation
Section intitulée « Onboarding & activation »| 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) |
Connexion (utilisateurs qui reviennent)
Section intitulée « Connexion (utilisateurs qui reviennent) »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) |
Coach — programme & leçons
Section intitulée « Coach — programme & leçons »| 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. reason ∈ url_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.
Aide à l’envie
Section intitulée « Aide à l’envie »| 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 |
Suivi & succès
Section intitulée « Suivi & succès »| 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 |
Paywall & premium
Section intitulée « Paywall & premium »| 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.
Notifications
Section intitulée « Notifications »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) |
Notation du store
Section intitulée « Notation du store »| 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) |
Le site kaiho.fr (hors app)
Section intitulée « Le site kaiho.fr (hors app) »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 (q1…q5), 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/26 — problem, 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é.
Le hero porte la page
Section intitulée « Le hero porte la page »À 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
PressStripcontinue 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é.
La cascade de secours vers l’App Store
Section intitulée « La cascade de secours vers l’App Store »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_started−safari_handoff - ouvert via Safari =
safari_handoff−panel_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.
Un seul badge store par plateforme
Section intitulée « Un seul badge store par plateforme »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.
Sans cookie, et ce que ça coûte
Section intitulée « Sans cookie, et ce que ça coûte »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
langqui 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.
Le smartlink /dl
Section intitulée « Le smartlink /dl »Aucun event. Le lien de téléchargement /dl (voir Lien de téléchargement) envoyait smartlink_clicked — retiré 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.