Appearance
Epic 40 — Account Tiers Restructure (Scout / Casting Director / Talent Free / Pro)
Reworks: Epic 12 (Agency Tier) and the live subscription system (
src/services/subscriptions/*). Decision (user, 2026-06-16): Two producer tiers — Scout (free) / Casting Director ($29.99/mo) — and two talent tiers — Talent Free / Talent Pro ($9.99/mo). Keep Agency ($49.99/mo); retire the olddirector($99.99) plan, folding its search + unlimited-post capability into Casting Director. Search is a paid feature for any active subscriber. Free producers keep the 1 job/month cap; Casting Director is unlimited.Status: ✅ Implemented 2026-06-16 (backend + frontend committed on
dev). Deploy steps: runscripts/migrate-director-plans.ts(normalizes legacy planKeys) andpnpm seed:subscription-plans(Stripe Prices; Casting Director reuses the existingcastyou_producer_monthly$29.99 Price). Decision-to-confirm: Casting Director monthly CasTars grant kept at 1,000 (its $29.99 price point); grandfathered $99.99 Director subscribers keep their Stripe Price until they next manage billing. FE-TIER-002 note: no standalone producer talent-search page exists yet, so there is no "dead search button" to hide today — the error-drivenUpgradeModalcovers the gate when search/browse is invoked.
Why: The shipped plan registry (PlanKey = 'talent_pro' | 'producer' | 'director' | 'agency') doesn't match the business model the stakeholders settled on. The names ("producer", "director") are wrong, search is gated to director/agency only (should be any subscriber), and there's no "Scout" vs "Casting Director" distinction. This epic renames/collapses the plan registry, re-targets every tier gate, migrates existing subscribers off the retired director key, and exposes the talent browse/search paywall (Epic 42's free swipe path is explicitly exempt).
Architecture: Source of truth stays src/services/subscriptions/plans.ts. New paid PlanKeys: casting_director, talent_pro (kept), agency (kept). The free tier is profile-aware: a producer with no subscription is Scout (monthlyPostCap: 1, canSearchTalents: false); a talent with no subscription is Talent Free (no swipe cap, canSearchTalents: false, no browse/search). Set canSearchTalents: true on all paid plans. quotas.ts logic is unchanged — only the caps move. Existing director/producer Stripe subscriptions migrate to casting_director (grandfather price until next renewal; map stripePriceId). Frontend gating stays error-driven (UpgradeModal, TIER_REQUIRED/QUOTA_EXCEEDED) plus proactive hiding of the search/browse entry points for non-subscribers.
BE-TIER-001 — Plan registry restructure + subscriber migration
Files:
- Edit:
castyou-backend/src/services/subscriptions/plans.ts— replacePlanKeyset withcasting_director | talent_pro | agency; define Casting Director ($29.99,monthlyPostCap: null,canSearchTalents: true, monthly CasTars grant); keep Talent Pro ($9.99) but flipcanSearchTalents: true; keep Agency ($49.99,canSearchTalents: true, roster limits); makeFREE_TIERprofile-aware (Scout:monthlyPostCap: 1; Talent Free: no swipe cap). Remove thedirector/producerplan entries. UpdateplanByStripeLookupKey()mappings. - Edit:
castyou-backend/src/http/stripeWebhook.ts+src/services/subscriptions/stripe.ts— map incomingdirector/producerlookup_key/planKeytocasting_director(back-compat) so existing webhooks/portals resolve. - Create:
castyou-backend/scripts/migrateDirectorPlans.ts— one-off: rewriteSubscription.planKeydirector/producer→casting_director; idempotent; logs counts. - Edit:
castyou-backend/src/services/subscriptions/quotas.ts— confirm caps read from the new registry (Scout=1, Casting Director=∞).
Acceptance criteria:
- Free producer posting a 2nd job in a calendar month →
QUOTA_EXCEEDED; Casting Director posts unlimited. - A subscription with legacy
planKey: 'director'resolves to Casting Director capabilities (no 500), and the migration script flips it permanently. - All paid tiers report
canSearchTalents: true; both free tiers reportfalse.
BE-TIER-002 — Search = any active subscriber
Files:
- Edit:
castyou-backend/src/graphql/resolvers/talent.ts—searchTalents: replace thedirector/agency-only check with "any active subscription" (getUserTier(...).canSearchTalents), keep admin bypass; update theTIER_REQUIREDextensions.requiredTiersmessage to "Casting Director, Talent Pro, or Agency".
Acceptance criteria:
- Talent Pro and Casting Director can
searchTalents; Scout and Talent Free getTIER_REQUIRED.
BE-TIER-003 — Talent jobs browse/search paywall (swipe stays free)
Files:
- Edit:
castyou-backend/src/graphql/resolvers/job.ts— gate the filtered browse/search path ofjobsForTalent(whenfilteris supplied / browse mode) behind an active subscription; leave the swipe feed (discoverJobs, Epic 42) ungated. EmitTIER_REQUIREDfor un-subscribed browse.
Acceptance criteria:
- A free talent can swipe the jobs feed but gets
TIER_REQUIREDwhen opening filtered browse/search; a subscriber gets results.
FE-TIER-001 — Plan naming, pricing page, upgrade copy
Files:
- Edit:
castyou-frontend/apps/app/src/hooks/useSubscriptions.ts+ the pricing page +components/UpgradeModal.tsx— render the four tiers as Scout / Casting Director / Talent Free / Talent Pro (+ Agency); remove "director" copy; mapcasting_directorlabel. - Edit:
castyou-frontend/packages/i18n/locales/{en,pt,es}/app.json— tier names, prices, upgrade strings.
Acceptance criteria:
- Pricing/upgrade UI shows the four named tiers (+Agency) with correct prices in all 3 locales; no "Director $99.99" remains.
FE-TIER-002 — Hide search/browse entry points for non-subscribers
Files:
- Edit: talent-search (producer) and talent jobs browse/search entry components — proactively hide/disable + show an upgrade prompt for free tiers (don't rely only on the error modal); keep the free swipe path visible.
Acceptance criteria:
- Free users don't see a dead search/browse button; subscribers do; the free jobs swipe is always reachable.
TEST-TIER-001 — Tier coverage
Files:
- Edit/Create:
subscriptions/quotas/searchTalentsbackend tests + pricing/upgrade frontend tests.
Acceptance criteria: Proves the new caps, the search-for-any-subscriber gate, the browse paywall + free-swipe exemption, and the director→casting_director migration.