Odessa → Audiobook — distribution plan (organic → paid)
Goal: take the "turn your imported journal into an inspiring fiction audiobook" creative from a static card to a measured, scalable paid channel — without spending a dollar before the funnel is measurable.
Audience (locked): consumer journalers (Day One / Journey migrants, r/Journaling), not EmberKiln authors. The funnel is:
ad impression
→ /odessa landing [odessa_landing_view]
→ CTA click [odessa_landing_cta_clicked]
→ signup [signup_completed]
→ import / first entry [journal_import_completed | first_entry]
→ Odessa seed [odessa_seed_generated]
→ story started [odessa_story_started]
→ ported to Graphene w/audio [odessa_story_ported_to_graphene {kick_off_audio:true}] ← north-star
The north-star conversion is a ported season with audio kicked off —
the moment an audiobook of the journaler's life actually exists. First-touch
UTM is attached to the PostHog person by PostHogProvider
(captureFirstTouchAttribution), so every rung is segmentable by campaign.
The honest constraint: this is a long funnel with a paid render at the end. Cold paid traffic into it is expensive until (a) every rung is measured and (b) the biggest drop-off (almost certainly time-to-first-audiobook) is reduced. Hence measurement-first.
Decisions locked (2026-06-12)
| Decision | Choice |
|---|---|
| Primary audience | Consumer journalers |
| First build | Phase 0 measurement instrumentation |
| First paid budget | Decide after Phase 2 — set from real funnel data + CAC ceiling |
Phase 0 — Readiness gates (before $1 of spend)
Instrumentation substrate already exists for the Studio/author funnel and is
reused here — pixels (FacebookPixel/TikTokPixel/RedditPixel/PinterestPixel,
gated on NEXT_PUBLIC_*_PIXEL_ID, mounted globally in app/layout.tsx),
first-touch UTM capture (src/lib/analytics.ts), and the ad-efficiency
admin dashboard.
- Top-of-funnel events on
/odessa—odessa_landing_view(withpaid+ traffic-source) andodessa_landing_cta_clicked(per-CTA). Shipped insrc/app/odessa/page.tsx+ event union insrc/lib/analytics.ts. This was the only fully-missing rung; everything downstream already fired PostHog events. - Share-card metadata —
src/app/odessa/layout.tsx(OG + Twittersummary_large_image) so shared/odessalinks render the promo card. - Paid-mode flag ON for the render path (gate from the Graphene Studio rollout plan).
- TOS / ad-policy pass — product reads personal journals; keep ad copy to "inspiring fiction audiobook," avoid therapeutic/mental-health claims (Meta/TikTok policy + app-store scrutiny).
- Landing sample audiobook — a real "press play" proof on
/odessaso a cold visitor hits the aha without journaling for two weeks first. (Point at an existing published season's audio.)
Pre-Phase-3 (needed before paid optimization, NOT before Phase 2)
- Fire pixel conversions on mid-funnel rungs —
odessa_seed_generatedand the north-star to Meta/TikTok viatrackFBConversion/trackTikTokEventso the ad algorithms can optimize toward them. Currently PostHog-only. - Server-side CAPI relay — none exists today (all pixel firing is
client-side). Add a Meta Conversions API relay in
apps/backendfor the north-star; server-side beats browser-only under iOS/ad-block.
Phase 1 — Organic / owned (free; validates the hook)
Ready-to-post kit: ODESSA_ORGANIC_KIT.md — UTM convention, per-channel captions, demo-video shot list, owned-surface copy.
- Push the card through owned surfaces: in-product, the
/dashboard/importsuccess screen (already has a "See your story with Odessa →" CTA — add the audiobook hook), and an email to existing journalers. - Post the 9:16 story cut as Reels/TikTok with a real screen-recorded demo (journal → story → press play); 1:1 in feed.
- A/B the 3 ranked headlines organically (variants in
apps/frontend/public/promo/build-promo.mjs): the winner becomes the paid creative. Free message-testing before spend.
Phase 2 — Measurement bake (~1–2 weeks of organic traffic)
- Build a PostHog funnel insight over the event sequence above; read each
rung's conversion %. Slice by
first_touch_utm_source/ campaign. - Find the biggest drop-off and fix it before paying to fill a leaky funnel.
- Compute LTV (render pricing + any subscription + Amazon-affiliate tail) → set a target CAC ceiling = LTV ÷ payback. Rule: don't spend until you can finish "a converted listener is worth $X."
- Then set the first paid budget.
Phase 3 — Paid test (small, structured)
- One channel first — recommend Meta (IG/FB); TikTok second, Reddit third (cheap, precise: r/Journaling, Day One).
- Test top-2 headlines × 2 formats, optimize to the north-star event (needs the pixel/CAPI items above), kill/scale criteria defined up front (CPA < CAC ceiling).
Phase 4 — Scale + iterate
- Scale winners; retarget landing-bouncers with the sample audiobook; lookalikes off converters; refresh creative against fatigue (the generator makes new cuts nearly free — a real moat for paid).
Where things live
- Creative + generator:
apps/frontend/public/promo/(build-promo.mjs,render-promo.sh) - Landing + funnel events:
apps/frontend/src/app/odessa/page.tsx - Share-card metadata:
apps/frontend/src/app/odessa/layout.tsx - Analytics utils + event union:
apps/frontend/src/lib/analytics.ts - Paid-traffic dashboard:
apps/frontend/src/app/dashboard/admin/ad-efficiency/page.tsx - Pixel env vars:
NEXT_PUBLIC_FB_PIXEL_ID,NEXT_PUBLIC_TIKTOK_PIXEL_ID,NEXT_PUBLIC_REDDIT_PIXEL_ID,NEXT_PUBLIC_PINTEREST_TAG_ID