Last updated: 2026-01-10
This document is grounded in the existing code paths in:
- Chat UI / triggers / upsell UI:
app/chat/[id]/chat-client.tsx - Server chat logic + prompt building + persistence helpers:
lib/chat-actions.ts - Message gating (tokens + premium upsell):
POST /api/deduct-token - Checkout creation:
POST /api/create-checkout-session,POST /api/create-upgate-checkout,POST /api/create-coingate-checkout - Payment confirmation:
GET/POST /api/verify-payment+POST /api/webhooks/*
In the current repo:
- The only explicit keyword trigger is
message.includes("video"). - Image intent is detected by
isAskingForImage(...).
So “custom” / “feet” are not implemented as triggers yet.
If you want them, implement them the same way as the existing video check (simple keyword detection + route to a gated flow).
Triggers (client-side):
- Video:
content.toLowerCase().includes("video") - Image:
isAskingForImage(content)
Upsell / paywall:
- Before sending a normal chat message, the client calls
POST /api/deduct-token.- If response includes
requiresPremium, the UI showsPremiumUpgradeModal. - If response includes
insufficientTokens, the UI showsInsufficientTokensDialog.
- If response includes
Payment-first:
- The upgrade happens on
/premiumwhich calls one of the checkout endpoints.
Delivery after confirmation:
- Once premium is active, gates stop blocking and premium-only features work.
Memory:
- Chat history is stored in Supabase table
messagesviasaveMessage(...). - Client also caches a copy in localStorage:
chat_history_${userId}_${characterId}.
Legend:
- Touchpoint column refers to the relevant code path / endpoint involved.
| # | Speaker | Message (example) | Touchpoint (in this repo) |
|---|---|---|---|
| 1 | User | hey | Chat UI (chat-client.tsx) |
| 2 | Bot | smiles warmly Hey there! I’m Mia. What’s your name? | Initial greeting logic in chat-client.tsx + saveMessage(...) |
| 3 | User | I want custom content. | Chat UI |
| 4 | Bot | leans closer What kind of custom… private picture, or something more personal? | sendChatMessage(...) in lib/chat-actions.ts |
| 5 | User | feet | Chat UI |
| 6 | Bot | (MVP trigger idea) “That’s private content. You’ll need to unlock it first.” | Proposed trigger detection similar to includes("video") |
| 7 | Bot | “Upgrade to Premium to unlock unlimited messages + private requests.” | /api/deduct-token returns requiresPremium → PremiumUpgradeModal |
| 8 | User | Okay, upgrade me. | Chat UI |
| 9 | Bot/App | “Opening checkout now.” | /premium → POST /api/create-checkout-session (or Upgate/CoinGate) |
| 10 | System/App | Checkout complete → redirect to /premium/success?session_id=... |
Premium success page flow |
| 11 | System/App | Verify payment / activate premium | /api/verify-payment + /api/webhooks/* updates premium status |
| 12 | User | I’m back. Can I get that custom feet content now? | Chat UI + history loaded from DB |
| 13 | Bot | “Mm… yes. Barefoot, socks, or heels?” | Premium gate no longer blocks (status is true) |
| 14 | User | barefoot, close up | Chat UI |
| 15 | Bot | (delivery) image / private content delivered | Image: /api/generate-character-image or Private: /api/unlock-bundle / /api/unlock-gallery |
A later session can use history to reference prior preferences:
- User: hey Mia
- Bot: “Hey… you came back. Still want barefoot close-ups, or should I surprise you this time?”
Why this is feasible in the current MVP:
- Messages are stored in
messages(Supabase) viasaveMessage(...). - The UI loads history using
getChatHistory(userId, characterId).
Chat UI + triggers (client)
- File:
app/chat/[id]/chat-client.tsx - Responsibilities:
- Trigger detection:
- video:
message.includes("video") - image:
isAskingForImage(message) - (optional future) private keywords:
custom,feet, etc.
- video:
- Payment/usage gating before sending:
POST /api/deduct-token
- Shows modals:
PremiumUpgradeModal,InsufficientTokensDialog
- Trigger detection:
Chatbot logic (server)
- File:
lib/chat-actions.ts - Responsibilities:
- Builds an enhanced system prompt (
enhancedSystemPrompt) - Sends to provider (Novita or ModelsLab)
- Persists messages via
saveMessage(...)
- Builds an enhanced system prompt (
Memory storage
- Primary: Supabase table
messages - Secondary (UI convenience): localStorage key
chat_history_${userId}_${characterId}
Payment confirmation + gating
- Create checkout:
POST /api/create-checkout-sessionPOST /api/create-upgate-checkoutPOST /api/create-coingate-checkout
- Confirm payment / activate premium:
GET/POST /api/verify-paymentPOST /api/webhooks/upgate,POST /api/webhooks/coingate
User types in chat UI
|
v
POST /api/deduct-token
|-- insufficientTokens --> show InsufficientTokensDialog
|-- requiresPremium ----> show PremiumUpgradeModal -> /premium checkout
|-- ok -----------------> continue
Trigger check (client)
|-- contains "video" ----> POST /api/generate-chat-video-kling
|-- isAskingForImage ----> POST /api/generate-character-image
|-- else ---------------> sendChatMessage(...) (server)
Save messages
-> Supabase table: messages (saveMessage)
After purchase
/premium -> create checkout -> /premium/success
-> /api/verify-payment + /api/webhooks/*
-> premium status active -> gates allow delivery
| Risk | What could go wrong | Mitigation (within scope) |
|---|---|---|
| Keyword triggers are brittle | False positives (any text containing “video”), or missed intent if phrasing differs | Tighten rules slightly while staying simple: require “show me” + “video”; require “show me” + “picture/photo”; require “custom/private” + keyword (“feet”). Log trigger hits for quick tuning. |
| Some premium gating is client-side | A modified client could try to bypass UI-only checks | Ensure server endpoints enforce premium too (not just UI). Validate premium in /api/generate-chat-video-kling, /api/text-to-speech, and any private-content delivery endpoints. |