Appearance
Phase 5 — In-Product Conversion Mechanics
Status: slices 1 & 2 BUILT; slices 3 & 4 not pursued. The cap instrumentation + near-cap nudge (slice 1) and the blocked-report paywall (slice 2) are implemented on the report hot path — no gate needed (they upgrade messages the bot already sends, and reuse the deployed Phase 4 button handlers). Reverse trial (slice 3) and Telegram Stars (slice 4) were explicitly descoped; the sections below are retained as future reference only.
Phase 5 is the inbound half of conversion: the moments inside the product where a FREE group meets the value ceiling. Where Phase 4 reaches out, Phase 5 converts at the point of friction — the blocked report and the near-cap report.
Goal: lift free→paid at the highest-intent moments, with the least friction, measured end-to-end. Objective unchanged: increase conversion to a paid plan.
Prime directive (live, paying product, real money)
Phase 5 touches billing state and the hot report path, so it's staged by risk and each customer-facing lever is flag-gated, default OFF, staging-first.
| Lever | Risk | Gate |
|---|---|---|
| Cap events + richer nudge/paywall copy | low (upgrades a message already shown) | roll in place, watch metrics |
| Telegram Stars checkout | medium (new payment path, additive) | STARS_ENABLED (default false) |
| Reverse trial | high (changes new-group entitlement + auto-downgrade) | TRIAL_ENABLED (default false) |
Hard rules: never grant a trial to a chat that already pays; the trial-expiry downgrade must never clobber a real subscription (PAYMENT=SUBSCRIPTION) or ETH credit balance; every money/permission mutation is idempotent and logged.
1. Cap-crossing instrumentation + nudges (low risk — ship first)
The gate already computes usage — utils/database/stats.mjs :: validateUserCredits (availableCredits = CREDITS − FETCHED_POSTS, XBotCredits FREE 100 / PRO 1000). It just doesn't emit or nudge well.
- Emit the cap events (types already exist, no emitters today): when a FREE chat's monthly
FETCHED_POSTScrosses 80% →CAP_80; when it reaches the cap →CAP_100. Fire once per period per threshold (idempotent — guard on aSEGMENTS/STATSmarker so a chat isn't spammed each fetch). These already drive thenear_cap/cappedclassifier segments and the Behavior funnel. - Upgrade the near-cap nudge at
commands/report.mjs:248(currently a bare"<n> credits left — open /setup → 💳 Buy Credits"): make it value-framed and digit-free (consistent with the agreed copy rules), with an inline[⬆️ Upgrade]button into/buy. Append it to the report itself (the moment the group is looking at the value they'd lose).
2. The paywall (highest-intent moment)
commands/report.mjs:84 currently sends a plain string when a report is blocked by the cap: "Your available credits have been exhausted. Please upgrade your plan…". Replace it with a real paywall:
- Value-framed, kind copy — why the group hit this (they're active!) + what PRO keeps flowing — no plan numbers.
- Inline
[⬆️ Upgrade to PRO]→/buy(or the new Stars flow, §4). - A 1-tap "what's holding you back?" micro-survey — reuse Phase 4's
survey:<key>:<option>handler verbatim (commands/outreach.mjs), keypaywall. Options: too expensive / not right now / missing a feature. - Emit
PAYWALL_SHOWNon display andSURVEY_RESPONSEon tap; the eventualLICENSE_CHANGEDwithin 14d is the conversion signal.
This is the single highest-leverage change in Phase 5 — a group that hit the cap has proven demand and is staring at a blocked deliverable.
3. Reverse trial (high risk — ship last, gated)
New groups can't feel PRO value on FREE. A reverse trial flips that: grant PRO on arrival, let them experience the ceiling-free product, then land the paywall when it ends.
- Grant on first activation (
BOT_ADDED→ first successful/setup), gated byTRIAL_ENABLED: setLICENSES.LICENSE=PRO+ new fieldTRIAL_UNTIL=<ms>(e.g. 7–14 days),PAYMENT='TRIAL'. EmitTRIAL_STARTED. Skip any chat that already hasPAYMENT=SUBSCRIPTION/CREDITSor a positiveREMAINING_CREDITS. - Expiry sweep: extend the nightly classifier Lambda (or a sibling) to find
PAYMENT='TRIAL'chats pastTRIAL_UNTIL→ downgrade to FREE (updateXBotLicense(id, FREE, preservePayment:false)— but only when stillPAYMENT='TRIAL', so it can't touch a chat that converted mid-trial), emitTRIAL_ENDED, and send the §2 paywall (framed as "your trial wrapped up"). - Mid-trial nudges: a "3 days of PRO left" style heads-up (digit-free variant) via the existing report-append path.
- Needs
licenses.mjschanges: astartTrial()grant + a trial-aware guard so the auto-downgrade logic inbilling.mjs(which already downgrades exhausted PRO·CREDITS chats) never fights the trial state.
4. Telegram Stars checkout (medium risk — additive)
commands/buy.mjs has exactly two providers today — Stripe (card/subscription) and ETH/BWS credits (payment_stripe_direct, payment_bws_direct). Neither is frictionless on mobile Telegram. Add Telegram Stars (XTR) as a third option:
- New
bot.action('payment_stars')→ctx.replyWithInvoice/sendInvoicewithcurrency: 'XTR'and a Stars price for the credit pack / PRO month. bot.on('pre_checkout_query')→ answertrue(validate first).bot.on('successful_payment')→ credit/upgrade the chat via the existing billing setters (setProLicenseWithSubscription/ credit top-up), emitLICENSE_CHANGED, confirm in-chat.- Gate the button behind
STARS_ENABLED. No change to the Stripe/ETH paths.
Stars is native, in-app, one-tap — the lowest-friction path for the mobile, crypto-native audience these groups skew toward.
5. Admin surface + measurement
- The Behavior funnel (Phase 6) already reads events; add a paywall→upgrade and trial→paid conversion tile, plus survey-reason breakdown, from
PAYWALL_SHOWN/SURVEY_RESPONSE/TRIAL_STARTED/TRIAL_ENDED/LICENSE_CHANGED. - Admin toggles for
TRIAL_ENABLED/STARS_ENABLED(env or a settings row) so rollout doesn't need a redeploy — mirror the Phase 4 master-switch pattern.
6. File-level task list
utils/database/stats.mjs— emitCAP_80/CAP_100fromvalidateUserCredits(idempotent per period).commands/report.mjs— richer near-cap nudge (:248) + real paywall (:84) with[Upgrade]+survey:paywall:*+PAYWALL_SHOWN.utils/database/licenses.mjs—startTrial(),TRIAL_UNTIL/PAYMENT='TRIAL', trial-aware downgrade guard; coordinate withbilling.mjsauto-downgrade.SegmentClassification_Handler.mjs(or sibling) — trial-expiry sweep.commands/buy.mjs+commands/outreach.mjs(survey reuse) — Stars invoice,pre_checkout_query,successful_payment.- IaC —
TRIAL_ENABLED/STARS_ENABLEDenv on the bot + relevant Lambdas. - Admin — conversion tiles + rollout toggles.
7. Rollout sequence
- Cap events + paywall/nudge copy (in place) → measure paywall view→upgrade.
- Enable Stars on a staging group → verify invoice → pre_checkout → payment → entitlement →
LICENSE_CHANGED; then enable in prod. - Enable reverse trial for new installs only, short window first; watch trial→paid and the expiry-downgrade safety (no subscription/credits touched); tune the window from the data.
8. Success metrics
Paywall view→upgrade rate, trial→paid rate, Stars share of checkouts, and overall FREE→PRO LICENSE_CHANGED lift — all already flowing through the Phase 1 event log into the Phase 6 admin funnel. Kill or retune any lever that doesn't move its metric.