The foundation worked out before any build: what each feature is, exactly how it runs live, which real provider powers it, what we reuse vs build, the design direction, and the decisions that gate everything. Grounded in the actual code (fe-main, main monorepo, justsext-landers), the live ElevenLabs config, Linear, and Slack — not the prototype.
Lure can be built almost entirely from systems that already exist in production. The lander lives in justsext-landers and reuses its attribution, Google auth, and analytics verbatim. The live voice call is the same ElevenLabs path the product runs today. Demo photos are served by a live endpoint from galleries that already exist for Sophie Dee. The work is mostly assembly + a new demo agent + a real design, not greenfield.
Three things are not ready and gate the monetizing parts:
ERT17-400 / UCF18) on the cheapest plan. A hard-paywall demo cannot convert until that is fixed. Owner: Klemen / Eric.Sophie Dee - Web's override whitelist disables client prompt + first_message and webhook-driven initiation. The oversell prompt and quiz opener cannot be injected at call-start as-is. A dedicated sophie-dee-voice-lander agent with overrides enabled is mandatory (this matches the Slack two-agent decision). Owner: Karen (EL admin).GTM-2177 is In Progress since Jun 22 but the branch ilia/gtm-2177-* is on no org repo. Sync with him before building or we duplicate/collide.Everything below treats those as decision points with options + a recommendation, so the spec is actionable now.
| Brief / prototype said | Reality (from code + Slack) |
|---|---|
| Payment via "Centrobill, no re-auth" | OpenPay is the gateway (Ilia confirmed). Code: getopenpay-go, saved payment_method_id one-click in lambdas/api-user-billing + api-webhook-openpay. "No re-auth" = reuse a stored payment-method token (exists on OpenPay). Centrobill has zero code; every "Centrobill" mention in the Lure tickets has been corrected to OpenPay. The only open payment item is the broken prod checkout, not the gateway choice. |
| Demo agent = a branch off the product agent ("not a separate agent") | The team decided the opposite in #eng (Jun 19-20): two separate agents, sophie-dee-voice-lander + sophie-dee-web. Reason: max call duration is an agent-level setting (confirmed in the EL config — max_duration_seconds is not client-overridable). The Linear ticket text (GTM-2178) is stale. |
| Reuse the prototype design | The prototype design is a throwaway. It reads as generic NSFW-AI-app "slop" (abstract orb, neon purple/pink gradients, casino clutter). It needs a full redesign toward a real, premium, creator-forward call (see §6). |
| "94% anonymous" is a problem to fix | For the lander it is by design — web demo calls start anonymous (pre-signup). The analytics join key is call_id, not user_id. Real account identity attaches at registration. |
One sentence: a Next.js lander on landers.justsext.com runs a live ElevenLabs voice call through the existing voice backend, gates the user, registers them with the same Google auth as the app, charges them through the live gateway, then hands off into the SIREN product via a one-time exchange code.
affid + ef_transaction_id captured by lib/attribution.ts (already built), persisted to localStorage. reusePOST /auth/auth/guest with the Everflow referral payload → guest JWT. Lets attribution attach before signup. reuseGET call.app.justsext.com/calls/signed-url?creator_id=… → {signed_url, agent_id, call_id, max_duration_seconds}. Browser connects to ElevenLabs via @elevenlabs/client Conversation SDK. port from fe-mainlib/auth.ts. reusePOST /auth/auth/exchange-code/create (30s one-time) → redirect justsext.com/?exchange_code=…&creatorId=…&startCall=true. App auto-logs-in and drops into the live sophie-dee-web call. reuse (backend ready)Throughout: every funnel step fires an event carrying affid + ef_transaction_id + creator_id (GTM-2182), aligned to the C78 dictionary, joined on call_id.
For each feature: how it works for real, the provider, what we reuse, what we build.
Not a mock. The browser holds a real WebRTC/WebSocket session with ElevenLabs Conversational AI.
@elevenlabs/client Conversation SDK (v1.2.0, same as the product). The lander is React, so re-implement the pattern from fe-main/src/app/shared/services/elevenlabs-call-provider.service.ts (it is Angular; lift the logic, not the file).GET call.app.justsext.com/calls/signed-url?creator_id=<id> with a Bearer JWT → returns signed_url, agent_id, call_id, max_duration_seconds. This call is the "call started" signal and starts billing server-side.call_id (+ creator_name) via conversation_initiation_client_data. Add user_account_id once D2C-5065 lands. The post-call webhook reads these back.onModeChange({mode:'speaking'|'listening'}) + a volume RAF loop. These drive the speaking animation and (later) the avatar with no extra work.max_duration_seconds from wallet balance and the SDK enforces it. For the demo, the cap is the agent-level setting on the new lander agent (see decisions).A new ElevenLabs agent, sophie-dee-voice-lander, cloned from Sophie Dee - Web (agent_9601kph9vs55ek4b08nj4bv86v03).
prompt / first_message. To set a short demo cap and inject the oversell opener, a dedicated agent is required. Decided in Slack; needs Karen to create it with overrides + conversation_initiation_client_data enabled.caFdfEzBHwcRadC1N5k0 on eleven_v3_conversational, LLM gpt-5.1, the 6 RAG scenario cards (the engagement engine), and the 4 eval criteria (engaged_call, reached_escalation, cta_delivered, no_premature_end).max_duration_seconds (the demo length), an oversell-tuned prompt (faster "holy shit" in 30s), and CTA copy pointing at register/paywall instead of the in-app photo button.Client-side state on top of the live call. Each maps to an in-product SIREN ticket so the behavior is defined, not invented. Put every mechanic behind a Statsig flag so the active set is config, not a deploy (GTM-2179 / GTM-2186).
| Mechanic | How it works | SIREN reference |
|---|---|---|
| Silence-rescue chips | 4s quiet → 3 cluster-aware reply chips; one tap fires a reply | D2C-4949 / D2C-4870 |
| Emoji react bar | tap → particle burst + creator audio ~200ms; ~50% random floating label | D2C-4623 |
| Photo slot-machine | 1.4s spin → locked image → unlock CTA (12 tokens); heat +10 request / +15 unlock; insufficient → paywall | D2C-4624 (full spec) · shipped code in D2C-4852 / fe-main PR #1215 |
| Heat meter | every interaction raises it; 50% confetti; 100% jackpot | D2C-4621 |
| Combo streak | back-to-back interactions stack an "x3 combo" chip | D2C-4622 |
| Token countdown | drains live; <60 red pulse; hits 0 → CC modal slides up, creator loops "don't go babe", no hangup | D2C-4548 epic |
| Cluster switcher | flip mid-flirt / heating-up / silent; pools + tone shift | D2C-4519 (canceled — reference only) |
Photos are not generated live. The "just generated" framing is a UI illusion over pre-gen assets (see §5). The slot-machine spin spec (1.4s, cubic-bezier, unlock at 12 tokens, heat math) is fully written in D2C-4624 — copy it.
POST api.justsext.com/auth/auth/google {code} → backend exchanges at Cognito, mints STXT access+refresh JWTs (claim stxt_user_id). Identity is keyed on the Cognito sub, so the lander account is the app account.justsext-landers/lib/auth.ts (googleAuth, buildGoogleOAuthUrl, signup) + app/auth/callback/page.tsx already implement this. Guest→user upgrade is automatic when the guest token is passed.GOOGLE_LOGIN_REDIRECT_URLS_ADDITIONAL allow-list, or /auth/google rejects it.payment_method_id on openpay_customer_id enables a one-click "no re-auth" charge → webhook credits tokens_balance.Two genuinely different paths. The call screen already exposes the attach point: swap the single creator <img> for a <video> driven by the existing isCreatorSpeaking + volume signals.
After purchase, mint a 30-second one-time code (POST /auth/auth/exchange-code/create, Bearer user token) and redirect to justsext.com/?exchange_code=…&creatorId=…&startCall=true. The app auto-redeems and starts the live sophie-dee-web call. Backend is ready; standardize on this (not the ?code= hack currently in the landers repo, which is a latent bug).
Events fire at every step (lander_view → demo_call_started → demo_chip_tap / emoji_react / photo_spin / photo_unlock / heat_milestone → demo_gate_reached → demo_registration_completed → demo_paywall_view → demo_purchase), each carrying affid + ef_transaction_id + creator_id, joined on call_id, aligned to the C78 dictionary. Owner: Mac, under Ilia (GTM-2182). Reuse the lander's existing Segment/GTM/PostHog rigs.
Two product bugs are still open: D2C-4943 (≈50% of web calls have null agent_id; 86.9% null creator_name all-time) and D2C-5065 (no user_id on web calls). If Lure ships its funnel before these land, demo events inherit null creator_id and purchases won't join back to source. Design a Lure-specific creator_id pass-through, or wait for the fixes.
| Capability | Provider / system | Endpoint / location | Status |
|---|---|---|---|
| Live voice | ElevenLabs Conversational AI + @elevenlabs/client | signed URL from call.app.justsext.com/calls/signed-url; backend main/services/call-service | live |
| Demo agent | ElevenLabs (new agent) | sophie-dee-voice-lander — to create, clone of agent_9601kph9vs… | to create (Karen) |
| Google auth | AWS Cognito (Google IdP) + STXT auth-service | api.justsext.com/auth/auth/google; reuse landers/lib/auth.ts | live |
| Handoff | STXT exchange-code | /auth/auth/exchange-code/create + redeem | backend live |
| Payment | OpenPay (confirmed — not Centrobill) | lambdas/api-user-billing + api-webhook-openpay | prod checkout broken |
| Photos | Pre-gen galleries (fal.ai Z-Image LoRA pipeline) | POST api/medias/internal/free-media by escalation_step; CDN static.justsext.com | live |
| Avatar (video) | fal.ai pre-render (rec) / Anam / d-id | fal.ai Kling/OmniHuman (team access set up) | M3 |
| Attribution | Everflow → lib/attribution.ts | captured + persisted on lander; snapshot on call create | live |
| Analytics | Segment + GTM + PostHog → BigQuery / Metabase | C78 dict; funnel.tools.stxt.ai | live (to wire) |
| Hosting | Vercel | justsext-landers, landers.justsext.com | live |
Creator: Sophie Dee. She is the one we test everything on, has the most ready content, the live EL agent, and a complete tagged gallery. Lock her as the launch creator (Eric's Jun 21 DM steer: pick 1-2 creators, move fast; pre-gen and live-gen both available).
Medium: go straight to video, skip photo-only testing. Ilia's call — video is more engaging than stills, so the demo content (the "slot machine" reveal, the creator hero, the escalation arc) should be short video clips, not photos. The photo-unlock mechanic stays as a shape, but what it reveals is video.
medias with escalation_step (1 sfw → 4 revealing/paywall → 5 nsfw), served via CloudFront / static.justsext.com. Sophie already has ~250 tagged stills to source frames/clips from.POST /medias/internal/free-media.Hard gate: Sophie Dee likeness/consent sign-off (Erin/legal) before generating motion video. Confirm before any gen run.
The current prototype is a throwaway placeholder. We rebuild the design from scratch and work it properly. But the engagement ideas stay — the whole point is to actively hook the user, so every mechanic and idea in the prototype is kept and tested, not thrown out. The redesign is not "remove gamification." It is two things: (1) raise the craft so every screen is clean, clear, and high-quality, and (2) produce many variants to test, because we don't yet know which shape converts cold traffic.
Plan for iterations, not one design. Two axes, and we build options along each:
Quality is the constant; style and density are the variables. The visual system should be derivable from the creator's brand, and we should ship several iterations so the team can tap through and the live A/B can read which shape wins.
Next step on design: I produce several real design directions in the actual lander stack (justsext-landers tokens / fonts: CHANEY-Wide, Funnel Display) — at minimum a casino-maximal and a clean-minimal, each at a couple of densities — built from scratch, not the old mock. You tap through and we pick what to productionize + A/B.
| Need | Lift from |
|---|---|
| Lander scaffold + deploy | justsext-landers (Next 15/16, Vercel landers.justsext.com). New route app/lure/ (+ v1..vN for A/B). |
| Everflow attribution | lib/attribution.ts (affid, ef_transaction_id, sub1-5, persisted) + lib/tracking.ts — verbatim. |
| Google auth + signup | lib/auth.ts + app/auth/callback/page.tsx + components/signup-form.tsx. |
| Live voice logic | fe-main/src/app/shared/services/elevenlabs-call-provider.service.ts (Angular → re-implement in React with @elevenlabs/client). |
| Photo slot-machine spec | D2C-4624 (spin timing, unlock, heat math) + shipped code in fe-main PR #1215 (D2C-4852). |
| Demo agent config | clone Sophie Dee - Web (prompt, voice, RAG cards, evals) → new sophie-dee-voice-lander. |
| Call visual shell (placeholder) | justsext-landers/app/valentine/_components/hero/ (caller-display, live-indicator) — presentation only; redesign per §6. |
| Handoff | exchange-code flow (/auth/auth/exchange-code/create) + app/go/_components/call-cta-button.tsx pattern. |
| Analytics rigs | lib/gtm.ts, components/posthog-provider.tsx, components/segment-provider.tsx. |
Each needs an owner sign-off. Recommendations are mine (Eric gave full latitude on Lure); the blockers need eng/EL confirmation.
max_duration_seconds on the new agent.sophie-dee-voice-lander (Slack-decided, required for cap + opener)prompt/first_message overrides + conversation_initiation_client_data enabled. Record the decision in GTM-2178.justsext.com/<path> — shares localStorage, no exchange-code neededlanders.justsext.com — must use exchange-code handoff/tools/send-sms funnel| Risk | Severity | Mitigation |
|---|---|---|
| Prod checkout broken (ERT17-400/UCF18); high card-validation-failure rate Jun 1-22 | critical | Fix before any paywall work; it's the entire monetization point. Confirm with Klemen. |
| Sophie-Web override whitelist blocks oversell prompt + quiz opener | critical | New agent with overrides enabled (Karen). Hard dependency before build. |
| Attribution join breaks (null creator_id D2C-4943 / no user_id D2C-5065) | high | Pass a Lure-specific creator_id explicitly, or sequence the funnel after the fixes land. |
| Duplicate/colliding work with Ruslan's unpushed shell | high | Sync with him + pull his branch before writing build steps. |
Cross-domain handoff latent bug (?code= vs exchange-code) | medium | Standardize on exchange-code; confirm /exchange-code/create is deployed on prod. |
| Placeholder Cognito + Segment secrets in landers env | medium | Get prod values from Ruslan / Vercel env before "live auth" claim. |
| Avatar NSFW/consent exposure (HeyGen bans; likeness sign-off) | medium | M3 only; pre-rendered path + Sophie consent first. |
sophie-dee-voice-lander agent + duration value (Karen) · Ruslan pushes his shell · prod env secrets.GTM-2177 shell before any build. (I can draft the message; won't send without you.)sophie-dee-voice-lander with overrides + initiation enabled, and lock the max-duration value (rec 120s) in GTM-2178/2240.GOOGLE_LOGIN_REDIRECT_URLS_ADDITIONAL.fe-main, the main Go monorepo, justsext-landers, the live ElevenLabs workspace, Linear (PROJECT LURE + SIREN tickets), and Slack (#eng / #war-room / #analytics). Code findings HIGH confidence; Slack decisions MEDIUM (decided in threads, not all mirrored in Linear). Identifiers and file paths cited inline are real as of 2026-06-23.