One page that shows every stage a teacher passes through, from the landing page to the first paid month, with the drop at each step and a warning where the drop is abnormal. It also records what is instrumented today on each surface, proposes the activation ("aha") moment and the retention levers, and lists the tracking work needed to make the numbers complete.
Proposal for discussion, not a decisionpartner-web source (branch feat/es-locale, 29/08/2026), the teado-landing-page source, a headless-browser check of the live sites, the backend schema in flyer-backends, and, once the extraction script has been run, the production read replica. Nobody on the product team has reviewed it. Treat the stage definitions, thresholds and recommendations as a proposal to correct, not as an agreed specification.
The teacher journey has eleven measurable stages. Eight of them can be counted today from the production database because the product already writes what it needs: the account row stores the landing UTMs and the source domain, the teacher profile stores every onboarding answer and every "viewed this feature" flag, and classes, assignments, AI-grading usage and subscriptions each have their own table. What cannot be counted today is the front of the funnel: teado.ai has no analytics at all, and teacher.teado.ai sends only page views to Google Analytics, so the landing-page drop and the signup-form drop are invisible.
Three findings shape the recommendations:
The proposed activation moment is "a real student's work comes back graded, inside the first session", reached in three steps after onboarding (one class, one student link, one assignment). The proposed retention lever is a weekly "what your students did" report that only exists when there is student activity, which pulls the teacher back to assign again. The tracking plan adds nine GA4 events and five user properties, links the landing and app domains, and turns on Google Signals for age and location.
Verified on 13/09/2026 by loading both sites in a headless browser and listing the third-party requests, then confirmed in source. The picture differs sharply between the two surfaces.
| Surface | Tool | Status | What it captures | Gap |
|---|---|---|---|---|
| teado.ai Next.js 16, Vercel project "teado" | Google Analytics / GTM | Absent | Nothing. No script, no dataLayer. | Landing visits, CTA clicks and the drop to signup are unknown. Every link to the app already carries utm_source=ldpteado with medium and campaign, so attribution exists on the app side only. |
| Facebook Pixel, Clarity | Absent | Nothing. | Ads cannot optimise on landing events. | |
| teacher.teado.ai partner-web, shared with teacher.flyer.us | Google Tag Manager GTM-WNL6H6VM | Live | Loads the tags below. dataLayer holds only gtm.js / gtm.dom / gtm.load; no product events are pushed. | All downstream tools see page views only. |
GA4 G-XWHVP4YV0E (in code) and G-P0HW70FSDY (from GTM) | Live | Page views on both properties; enhanced-measurement auto events if enabled in the property. | No signup, onboarding, class, assignment or purchase events. Two properties receive the same hits; which one the team reads is not documented. Cross-domain with teado.ai is not configured because teado.ai has no tag. | |
Microsoft Clarity n8orfkthqt | Live | Session recordings, heatmaps, rage and dead clicks, quick-backs, JS errors per URL. | Not used for the funnel yet. Identify API not called, so a recording cannot be joined to a teacher. | |
Google Ads conversion tag 976672787 | Live | View-through conversion on page load. | No purchase or signup conversion is sent, so bidding optimises on visits. | |
Facebook Pixel 1005592880615993 | PageView only | PageView. | No CompleteRegistration, no Purchase; no Conversions API. | |
| Datadog RUM + Logs | Live | Route changes as actions, automatic click tracking, 20% session replay, errors. | Engineering tool; the product team does not read it. | |
| Backend (api.flyer.us) | Mixpanel | Disabled | Initialisation is commented out in _app.tsx; three track() calls remain and do nothing. | Remove or revive; today it is dead code. |
| Product database | Complete | users keeps source domain, auth provider, country and UTMs; teacher_profiles.onboarding_info keeps every onboarding answer and every viewed_* milestone; classes, assignments, teacher_usage_ledger (AI quota used) and subscriptions are separate tables. | This is the source of Section 3. It cannot see visitors who never signed up. |
One row per stage, counted on teachers who signed up in the last 90 days. Each bar is the share of the first stage; the label shows the count and the conversion from the previous stage. A stage is flagged when its step conversion falls under the threshold in Section 4, and the largest single drop is marked. Hover a bar for the definition.
funnel.json is missing. The rows below show the stage definitions; Section 9 explains the one command that fills them.Same window and the same stage definitions for both sources.
| Load funnel.json to fill this. |
Stages 1 to 3 are invisible until the Section 7 events exist. Stages 4 to 14 are computed from the replica with the fixture filters applied.
utm_source=ldpteado&utm_medium=button&utm_campaign=header|hero. Needs a cta_click event. Not counted today./signup and chose Google or phone. Needs sign_up_start. Not counted today.users row, Teado-origin when source_domain or utm_source contains "teado". Auth provider is stored (Google or phone OTP).users.state active, or a teacher profile exists. Phone signups pass an OTP screen (/verify-account) first./onboarding calls viewerCreateTeacherProfile, which creates the teacher_profiles row. This is the first "create account" step in the request.onboarding_info contains number_of_students, school_name, is_freelance_teacher or what_curriculum_teaching. Distinguishes teachers who filled the form from those who skipped it.viewed_* flag (exam library, classes, students, program, AI grading). The app writes these itself.assign_tests with is_sample=false. The ten sample assignments every teacher receives are excluded. Measured 02/10/2026: 62 of 617 Teado teachers, and 51 of those 62 had never created a class of their own.classes with is_sample=false created at least 10 minutes after the profile. Only 14 Teado teachers did this in 90 days, because the auto-created sample classes already give them somewhere to assign. This step sits after the assignment on purpose: it is the rarer act, not the earlier one.viewed_ai_grading_speaking or viewed_ai_grading_writing marker in onboarding_info. The original definition used teacher_usage_ledger.used > 0, which is unusable: that table holds exactly one row for the entire product. This stage therefore measures reaching the screen, not completing a grading run.user_attempts row pointing at one of the teacher's non-sample assignments. This is the proposed aha moment (Section 5). The original definition used profile_completed_ids, which is populated on only 89 of 60,676 non-sample assignments and repeats the same learner id dozens of times, so it understated this stage to nearly zero.teacher_profiles.trial_expires_at is set. The current offer is a 7-day trial.teacher_subscriptions row pointing at a teacher_packages entry priced above zero . The two live packages are TEADO_TOOL at 2,490,000 VND and TEADO_TOOL_PLUS at 4,800,000 VND, both for 365 days. Zero-price packages count as trial, not revenue. No purchase is observable yet: the whole subscriptions table holds one row, and one activation code has ever been used.Signups by week with the count reaching each key stage, so a change in the funnel can be dated. Recent weeks are not mature: a cohort needs about four weeks before its assignment and paid counts settle.
| Week of | Signups | Profile | Class | Assignment | AI use | Student submit | Paid |
|---|---|---|---|---|---|---|---|
| Load funnel.json to fill this table. | |||||||
| Onboarding answers appear here after the script runs: role, freelance or school, number of students, curriculum, how they heard of Teado. |
| UTM source / medium / campaign | Signups | Profile | Assign | Paid |
|---|---|---|---|---|
Filled from users.utm_*. | ||||
| Sign-in method | Signups | Profile | Assign |
|---|
| Country (users.country_code) | Signups | Assign |
|---|
A drop is only a problem relative to what the step should convert. The thresholds below are starting values taken from typical self-serve B2B SaaS onboarding; the team should replace them with Teado's own trailing 90-day median once three months of data exist. The page applies them automatically.
| Step | Watch under | Critical under | Usual cause when it is low |
|---|---|---|---|
| Landing visit to CTA click | 8% | 4% | Message does not match the ad; CTA below the fold on mobile; language switch mid-page. |
| CTA click to account created | 50% | 30% | Phone OTP delivery; Google button fails on in-app browsers (Zalo, Facebook); form asks too much. |
| Account created to onboarding form submitted | 85% | 70% | Role choice unclear; school picker slow; required "Họ" field; page reload loses state. |
| Onboarding submitted to first real class | 45% | 30% | The guided step is skipped on Teado (lite default); the library is shown instead of the class wizard. |
| First class to first real assignment | 60% | 40% | Empty class (no students); library filters; the ten sample assignments make the list look already full. |
| First assignment to first student submission | 50% | 30% | Students never received the class code; assignment scheduled too far ahead; parents, not students, hold the device. |
| Student submission to trial claimed | 40% | 25% | Trial is offered before value is felt; quota exhausted without explanation. |
| Trial to paid | 15% | 8% | Price shown per day (13,000 VND/day) versus per package; payment only by transfer or VNPay; no reminder before expiry. |
| Retention week 2 / week 4 | 40% / 25% | 25% / 15% | No reason to return: nothing new from students; no weekly summary; assignments are one-off. |
Clarity already records every teacher.teado.ai session. The signals below, read per URL in the Clarity dashboard, tell whether a low step is a design problem or a demand problem. Until the Identify call in Section 7 is added, recordings cannot be joined to a teacher, but the per-URL rates already work.
| Screen | Signal | Warn when | Reads as |
|---|---|---|---|
/signup, /verify-account | Quick-back rate, JS errors | quick-back > 20%; any error spike | OTP or Google login failing; a design fix, not a marketing fix. |
/onboarding | Dead clicks on the role cards; rage clicks on "Tiếp tục"; scroll depth | dead clicks > 5% of sessions; rage clicks > 2% | The form blocks on a validation the user cannot see (required last name, school not selected). |
/explore (first landing after onboarding) | Exit rate on first visit; time to first click | exit > 40% within 30 s | The library is not a next step; the teacher does not know what to do. Supports Section 5. |
/onboarding/create-class, /classes | Abandon between "create class" and "add student" | > 50% leave after class creation | Adding students is the friction; the class code sharing flow needs a Zalo-ready message. |
/flyer-ai (AI grading) | Rage clicks on upload; JS errors | any | Upload or quota errors; the quota message must say what is left and how to get more. |
/package, /billing | Exit rate; dead clicks on price cards | exit > 70% | Price or payment method, not interest. Compare with trial-to-paid. |
The product sells time saved on grading and reporting. A teacher believes that only when a student's work comes back marked without the teacher doing it. So the proposed activation event is: a real student submits an assignment and the teacher opens the graded result, within the first session or the first 48 hours. In the database this is stage 12 (first non-sample assignment with a completed profile) plus the teacher viewing it, which the tracking plan adds as result_viewed.
Why this and not "created a class" or "used AI grading once": a class without students is an empty room, and one AI grading on the teacher's own upload proves the model works but not that the workflow fits a classroom. The submission is the first moment the product did work the teacher did not do.
router.replace(EXPLORE + (isLite ? '' : '?showWelcomeV3Modal=1')) drops the welcome modal for lite users. Showing it on Teado is a one-line change and puts every new teacher in front of "create your class" instead of a library of 6,000 tests.what_curriculum_teaching), pre-fill one student as "me", and let the teacher submit as that student to see the graded result at once. This produces stage 12 inside the session, then invites real students.Target to validate: the median profile to first submission time falls under 48 hours, and the share of Teado signups reaching stage 12 within 7 days doubles from its baseline. Both numbers are on this page once the script has run (Section 3, time-to-stage tiles).
Teacher churn on FLYER is already measured: individual teachers churn at 84.8% year on year while B2B centres churn at 17.4% (data.flyer.vn/teacher-metrics). Teado sells to the individual teacher, so the retention design has to create the habit a centre imposes by contract: assign every week, look at results every week.
| Lever | What it does | Why it fits the current product | Measure |
|---|---|---|---|
| Weekly student report | Every Monday a summary of submissions, scores and the students who did nothing, with one button "assign this week's test". | The data already exists (assignments, attempts, monthly report screen). The report is empty for teachers with no students, which itself nudges stage 9–10. | Week-2 and week-4 active rate (Section 3 tiles). |
| Assignment schedule | Let the teacher set a weekly slot once; the app proposes the next test each week. | assign-schedule and viewed_assign_schedule already exist; the feature needs a default, not a build. | Share of teachers with two or more assignments in 30 days. |
| Trial that starts at value, not at signup | Start the 7-day trial when the first real submission arrives, and say what the trial unlocks in the teacher's own numbers ("120 speaking recordings left"). | trial_expires_at and can_claim_trial are per profile; the ledger knows the quota. | Trial-to-paid conversion. |
| Expiry and quota messages with a next action | Three days before expiry and when a quota reaches 80%, an in-app message with the package that matches their usage. | The lite version redirects blocked routes to / silently today; a teacher who hits a limit should see why. | Paid conversions within 7 days of the message. |
| Student-side pull | Students and parents get the result notification with the teacher's name, so the teacher is asked "when is the next test". | Notifications exist on the learner side (7.5M rows); this is a template, not a feature. | Repeat assignment rate. |
| Win-back at day 21 | If no action for three weeks, one message that shows what changed in the library for their curriculum. | Curriculum answer stored in onboarding; library has level and curriculum tags. | Reactivation rate. |
The principle: the database stays the source of truth for anything that happens after signup, and Google Analytics is added for the part the database cannot see (visitors, clicks, form starts) and for audience data (location, age, device). Nothing is built from scratch; the tags exist, they need events.
@next/third-parties/google with the same GTM container GTM-WNL6H6VM so both surfaces report to one GA4 property, and configure cross-domain measurement for teado.ai and teacher.teado.ai in the GA4 data stream. Without this a landing visit and the signup it produced count as two users.cta_click (cta_location: header | hero | footer, lang) on every link to the app. The links already carry UTMs; the event makes the click itself countable.view_section (hero, features, testimonials, pricing) at 50% visibility, to see where readers stop before the CTA.One helper, track(event, params), that pushes to window.dataLayer and lets GTM forward to GA4, Facebook and Google Ads. Nine events cover the funnel:
| Event | Where it fires | Parameters | Marks as conversion |
|---|---|---|---|
sign_up_start | /signup when Google or phone is chosen | method | no |
sign_up | after viewerSignUp succeeds | method, host (teado | flyer) | GA4, Facebook CompleteRegistration, Google Ads |
onboarding_submit | after viewerCreateTeacherProfile | role, is_freelance, number_of_students, curriculum | no |
class_created | CreateClassStep success | is_first | no |
student_added | AddStudentStep success | count, method | no |
assignment_created | assign success (non-sample) | is_first, test_level | GA4 key event |
ai_grading_used | speaking or writing grading returns | feature_code, is_first | no |
result_viewed | teacher opens a graded submission | is_first | GA4 key event (activation) |
purchase | subscription code applied / payment confirmed | package_code, value, currency, transaction_id | GA4, Facebook Purchase (Pixel + Conversions API), Google Ads |
Five user properties set once after login, so every report can be split by them: role, is_freelance, school_size, curriculum, teado_version (lite | full). They come from the same profile object the app already loads.
country_code per user, shown in Section 3.clarity("identify", teacher_profile_id) after login, and clarity("set", "stage", "<last stage reached>"), so a recording can be filtered to "teachers who created a class and never assigned".clarity("event", "same name as the GA4 event") from the same track() helper; Clarity then shows funnels of its own with rage and dead clicks per step.The extraction script in Section 9 produces this page. When the events above exist, the same script can pull the top-of-funnel counts from the GA4 Data API (or from the BigQuery export prepared earlier at admin.teado.ai) and prepend them to the same JSON, so one page keeps showing the whole path.
purchase and sign_up instead of page load.Each idea uses something that already exists in the code or the data. They are ordered by expected effect on the funnel over engineering cost.
/ without a message. New teachers cannot know what they are missing. Keep the quota, drop the route limits, or at least replace the silent redirect with a screen that names the feature and the package.is_freelance_teacher). Freelancers have no centre forcing a schedule, so they need the weekly report and schedule defaults most; the B2B path (school portal, invite teachers) should not be shown to them.referral_code, ref_teacher_id, ref_completed_tasks) but not surfaced in the Teado flow. A "invite a colleague, both get a week" card after the first result costs a template.The page reads /teado-funnel/funnel.json. The script that writes it lives outside the site so that database credentials are never deployed. Run it from a terminal on the analytics machine:
python3 ~/teado-funnel-build/funnel_query.py # 90-day window, 12 weekly cohorts DAYS=180 WEEKS=26 python3 ~/teado-funnel-build/funnel_query.py # longer view cd ~/Downloads/roadmap.flyer.us && npx wrangler pages deploy . --project-name=flyer-roadmap --commit-dirty=true
The script prints the distinct source_domain values it found, so the Teado filter (source_domain or utm_source containing "teado") can be checked against reality on the first run. It excludes sample classes and assignments, classes created within 10 minutes of the profile, and accounts that only have a learner profile. A cron entry can run it daily once the definitions are agreed; the other data pages on data.flyer.vn already use that pattern.
partner-web; add Clarity identify.Sources: partner-web (src/pages/_app.tsx, _document.tsx, onboarding, providers/teado, constants.ts, hooks/useTeacherOnboardingInfo.ts), teado-landing-page (lib/i18n/dictionaries.ts), headless-browser request log of teado.ai and teacher.teado.ai on 13/09/2026, flyer-backends/flyer-go/db/schema/current.sql, memory notes on sample fixtures and teacher churn. Related: document index · teacher metrics · admin.teado.ai (BigQuery queries) · Roadmap