setup-guides

EmberKiln — paid launch go-live runbook

The code for paid Studio is fully merged and inert until one flag flips: usage/credits billing (one-time trial → credit packs, no subscription), a render paywall, recorded terms consent, and an activation funnel. This is the exact sequence to take it live. None of it changes closed-beta behavior until step 4.

Model decisions (locked 2026-06-12): usage/credits, no subscription; one-time free trial (not recurring); legal consent gate on every creation path. See docs/ai/INDEX.md → "Studio paid-mode enforcement" for the full code map.

PRs: #545 (enforcement), #546 (paywall UX), #547 (consent), #549 (funnel), #550 (consent on all creation paths).


0. Prerequisites

  • Stripe account in live mode (Graphene+ already uses it).
  • Access to Railway (backend service) and Vercel (frontend) env vars.
  • Super-admin account for the readiness + funnel dashboards.

1. Apply migrations to prod

Apply via your normal migration process (see CI_CD_SETUP.md; MCP does NOT point at prod):

  • 359_studio_terms_acceptance.sqlprofiles.studio_terms_{accepted_at,version}
  • 360_studio_activation_funnel.sqlprofiles.creator_granted_at + story_seasons.published_to_graphene_at (+ backfill + indexes)

2. Stripe — create the two credit-pack prices (live mode)

The packs are defined in code (studio-credit-packs.ts CREDIT_PACKS). The chars credited come from the code's catalog, not the Stripe Price — the Price only controls the charge amount, so it MUST match exactly:

  • Product "Studio Credits — 100k characters" → one-time $50.00 → copy Price ID
  • Product "Studio Credits — 500k characters" → one-time $200.00 → copy Price ID

3. Set env vars

Stripe vars → Railway only (backend). They're server secrets; the frontend never calls Stripe directly. If Graphene+ is live, the first two already exist — only the two STRIPE_PRICE_STUDIO_CREDITS_* are new.

# Railway (backend service)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...              # the live webhook signing secret
STRIPE_PRICE_STUDIO_CREDITS_SMALL=price_...  # the $50 / 100k price
STRIPE_PRICE_STUDIO_CREDITS_LARGE=price_...  # the $200 / 500k price
STUDIO_TRIAL_CHARS=20000                     # optional; default 20000

Webhook: the same /api/payment/webhook endpoint handles credit packs (it branches on metadata.kind === 'studio_credit_pack'). Confirm its live event list includes checkout.session.completed — no second endpoint needed.

Verify everything green at /dashboard/admin/studio-launch (super-admin).

Cosmetic gotcha: that page's price labels still read "500k / 1.5M" from a pre-repricing era — ignore the label text; the real packs are 100k/$50 and 500k/$200. Only "var is set" matters.

4. Flip the launch flag — both Vercel and Railway

NEXT_PUBLIC_STUDIO_PAID_MODE must be set in both places, then redeploy both:

  • VercelNEXT_PUBLIC_ is inlined into the client bundle at build time; the frontend copy/paywall only flips if it's set here + redeployed.
  • Railway — the backend mirror (studio-mode.ts) reads it at runtime to switch on trial grants, the 402 preflight, and the consent gate.
NEXT_PUBLIC_STUDIO_PAID_MODE=true

Setting only one = split brain: Vercel-only → UI says "paid" but nothing is enforced; Railway-only → enforcement is live but copy still says beta.

5. Smoke-test on staging first (flag on)

  1. Sign up fresh → claim creator (/studio/new) → consent gate appears.
  2. Accept → confirm the 20k trial seeded (/studio/billing shows balance).
  3. Render past the trial → 402 paywall modal → click a pack → Stripe Checkout (test card 4242 4242 4242 4242) → land back on /studio/billing, balance bumped by 100k/500k.
  4. In Stripe → Developers → Webhooks → recent deliveries: checkout.session.completed returns 200. If the balance didn't move, check the delivery response + backend logs for [studio-credit-packs].
  5. Export an M4B + publish → check /dashboard/admin/studio-funnel shows the cohort advancing.

6. Go live + run the pilot

  • Repeat steps 2–4 in live mode for prod.
  • Run a small instrumented ad pilot to the ICP (mid-list ACX authors paying narrators $200–400/finished-hour). Read activation at /dashboard/admin/studio-funnel; slice signup→creator by ad source in PostHog (studio_creator_granted event carries first-touch UTM).

Rollback

Set NEXT_PUBLIC_STUDIO_PAID_MODE=false on both Vercel + Railway and redeploy. The trial/paywall/consent gates all no-op immediately; the DB columns and any seeded trial credits are harmless when the flag is off. Existing closed-beta creators are unaffected throughout (they're grandfathered on the rolling-quota model).

Open / non-code items

  • Attorney pass on /studio/terms — currently self-authored (the page says so). If the reviewed copy changes anything material, bump STUDIO_TERMS_VERSION in studio-terms.ts to force re-acceptance. Recommended before scaling spend.
  • Funnel caveat: creator_granted_at / published_to_graphene_at are backfilled approximately for pre-migration-360 rows; the pilot cohort is exact. signup→creator is all-signups (no studio-intent on signup) — use PostHog UTM cohorts for per-campaign attribution.
EMBERKILN PAID LAUNCH — Docs | HiveJournal