Skip to content

Demo Specs Discussion

Yep, this is very buildable in 3 days. Let’s lock in a concrete, opinionated spec so you don’t lose time re-deciding things mid-sprint.

I’ll cover:

  1. Demo user flow (end-to-end)
  2. Tech stack
  3. Data model
  4. Routes & APIs
  5. Key components / UX
  6. Day-by-day implementation plan

1. Demo user flow (what YC will actually see)

From YC’s perspective:

  1. Go to AI Wallet landing/dashboard (/wallet)

  2. They click “Sign in with Google”.

  3. They land on a simple Wallet dashboard:

    • “Hi, founder@...
    • “Balance: $5.00 demo credits”
    • “Recent activity” table — currently empty or with one seeded item.
    • Button: “Try AI Wallet in a demo app”/try.
  4. Go to demo chatbot app (/try)

  5. UI looks like a different app (simple brand variation).

  6. At top: Login with AI Wallet button (your “SDK”).

    • If already logged in on wallet, clicking it just “authorizes” and closes a small modal.
    • Once logged in:

    • Header: “Wallet: $5.00 demo credits remaining”.

    • Model dropdown: e.g. gpt-4.1, claude-3-5 (from OpenRouter).
    • Chat box with streaming responses.
  7. They send a message

  8. You:

    • Call OpenRouter via Vercel AI SDK.
    • On server, decrement, say, $0.05 in demo credits.
    • Create a usage event row: who, app, model, amount, timestamp, prompt snippet.
    • Frontend:

    • Streams assistant answer.

    • Wallet display updates to $4.95.
    • Maybe a tiny inline “Last call: –$0.05 demo credits”.
  9. They go back to /wallet

  10. The dashboard now shows:

    • Balance: $4.95.
    • Activity row:

    • 2025-11-08 10:21 | Demo Chatbot | gpt-4.1 | –$0.05.

    • YC sees: same user, two apps, one wallet, unified history.

That’s the core story. Everything below is in service of that.


2. Tech stack (opinionated but simple)

Frontend / Backend:

  • Next.js 14 (App Router) + TypeScript.
  • Deployed on Vercel.

Auth:

  • next-auth with:

  • Provider: Google OAuth (only).

  • Session strategy: JWT (simple enough for demo).

Database:

  • PostgreSQL via Prisma (or Drizzle if you prefer):

  • Hosted: Supabase / Neon / Railway — whatever you’re familiar with.

LLM:

  • OpenRouter HTTP API.
  • Vercel AI SDK (ai package) for streaming in /api/chat.

Styling:

  • Tailwind CSS (fast to iterate).
  • Very simple light UI, no design rabbit holes.

3. Data model

Keep it minimal but expressive enough for receipts.

3.1 Entities overview

You really only need:

  1. User
  2. App (logical app using AI Wallet)
  3. WalletTransaction / UsageEvent

Optional but nice:

  1. ChatSession (to group chat messages)
  2. ChatMessage (store user & assistant messages)

If pressed for time, you can skip persisting ChatSession/ChatMessage and only store usage events + a prompt snippet.

3.2 Tables

I’ll phrase these as Prisma-style, but you can map to anything.

User

model User {
  id           String   @id @default(cuid())
  email        String   @unique
  name         String?
  image        String?
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  demoCredits  Int      @default(500) // e.g. 500 = $5.00 if 100 units = $1

  usageEvents  UsageEvent[]
}

Use an integer credits unit to avoid floating-point headaches: 100 credits = $1. So $5 demo = 500 credits.

App

model App {
  id          String        @id @default(cuid())
  slug        String        @unique // 'wallet-dashboard', 'demo-chatbot'
  name        String
  description String?

  usageEvents UsageEvent[]
}

Seed two records:

  • wallet-dashboard
  • demo-chatbot

UsageEvent

This is your “consent/usage receipt” for the MVP.

model UsageEvent {
  id             String   @id @default(cuid())
  user           User     @relation(fields: [userId], references: [id])
  userId         String
  app            App      @relation(fields: [appId], references: [id])
  appId          String

  // Demo “ledger”:
  creditsDelta   Int      // typically negative numbers for debits
  creditsAfter   Int      // balance after this event, for easy display

  // LLM details:
  model          String
  tokensEstimate Int?
  promptSnippet  String?  // first 100–200 chars of user prompt

  createdAt      DateTime @default(now())
}

Every chat turn → one UsageEvent row.

(Optional) ChatSession / ChatMessage

If you want to show past chats later, add:

model ChatSession {
  id        String        @id @default(cuid())
  user      User          @relation(fields: [userId], references: [id])
  userId    String
  app       App           @relation(fields: [appId], references: [id])
  appId     String
  title     String?
  createdAt DateTime      @default(now())

  messages  ChatMessage[]
}

model ChatMessage {
  id          String       @id @default(cuid())
  session     ChatSession  @relation(fields: [sessionId], references: [id])
  sessionId   String
  role        String       // 'user' | 'assistant'
  content     String
  model       String?
  createdAt   DateTime     @default(now())
}

But this is optional for the 3-day demo.


4. Routes & APIs

4.1 Pages

Public / main:

  • / → either:

  • redirect to /wallet, or

  • simple marketing with CTA “Go to your wallet” → /wallet.

Wallet dashboard app:

  • /wallet

  • Requires auth.

  • Shows:

    • User info.
    • Current credits (demoCredits).
    • “Recent activity” table from /api/wallet/usage.
    • Button “Try AI Wallet in a demo app” → /try.

Demo chatbot app:

  • /try

  • Public route.

  • Shows:

    • A distinct header (e.g. “Demo Chatbot App” brand).
    • <LoginWithAIWalletButton />.
    • If user authorized:

    • Wallet balance display (pulled from /api/wallet/me).

    • Model dropdown.
    • Chat UI.
    • Chat uses /api/chat endpoint.

4.2 Auth routes

  • /api/auth/[...nextauth]

  • Google provider.

  • On first sign-in:

    • Create User row with initial demoCredits = 500 (=$5).
    • Session includes userId.

GET /api/wallet/me

Returns current user wallet info.

Input: session from NextAuth. Output:

{
  "userId": "xxx",
  "email": "user@example.com",
  "name": "User",
  "demoCredits": 485
}

Used by /wallet and /try to display balance.

GET /api/wallet/usage

Returns recent usage events for the logged-in user.

Query params:

  • limit (optional, default 20)
  • maybe appSlug (optional) if you want to filter by app later.

Response:

[
  {
    "id": "...",
    "createdAt": "...",
    "appName": "Demo Chatbot",
    "model": "gpt-4.1",
    "creditsDelta": -5,
    "creditsAfter": 495,
    "promptSnippet": "Explain what AI Wallet is..."
  },
  ...
]

Used to populate the “Activity” table on /wallet.

POST /api/wallet/topup-demo (optional)

If you want a “Reset demo balance” button:

  • Sets demoCredits back to 500.
  • Adds a UsageEvent with positive creditsDelta as a “top-up” event.

4.4 Chat API

POST /api/chat

Handles:

  • Validating the user/session.
  • Charging demo credits.
  • Logging a UsageEvent.
  • Calling OpenRouter via Vercel AI SDK.
  • Streaming back the completion.

Request:

{
  "appSlug": "demo-chatbot",
  "model": "openrouter/gpt-4.1",
  "messages": [
    { "role": "user", "content": "Explain what AI Wallet is." },
    ...
  ]
}

Server-side steps (simplified):

  1. Authenticate user via session.
  2. Fetch User and current demoCredits.
  3. Decide cost:

  4. For demo: fixed cost per call, e.g. COST_PER_CALL = 5 credits (=$0.05).

  5. Or approximate from messages length.
  6. If demoCredits < COST_PER_CALL, return 402-style error: “Insufficient demo credits”.
  7. Subtract credits, compute newBalance = demoCredits - COST_PER_CALL.
  8. Create UsageEvent:

  9. appId for demo-chatbot.

  10. creditsDelta = -COST_PER_CALL.
  11. creditsAfter = newBalance.
  12. model, promptSnippet (first ~160 chars of last user message).
  13. Save User.demoCredits = newBalance.
  14. Call OpenRouter with the messages and stream back to client using Vercel’s StreamingTextResponse / AiStream.

On the client:

  • Start streaming response.
  • Immediately update wallet display using the new balance from the response (include it in the initial JSON chunk or add a side-channel call to /api/wallet/me when finished).

Response shape (non-streaming version for conceptual clarity):

{
  "model": "openrouter/gpt-4.1",
  "answer": "… streamed in reality …",
  "tokensEstimate": 200,
  "creditsCharged": 5,
  "newBalance": 495
}

But with streaming, you’d usually:

  • Send a small JSON preamble (with newBalance) then stream tokens.

5. Key components / UX

5.1 <LoginWithAIWalletButton />

This is your “SDK-like” piece.

Props:

type LoginWithAIWalletButtonProps = {
  onAuthorized?: () => void;
  appSlug: 'demo-chatbot';
};

Behavior:

  • If user isn’t logged in:

  • Triggers NextAuth sign-in with Google (or a small modal that says “We use AI Wallet for login” then sign-in).

  • If user is logged in but app “not authorized”:

  • For demo, you can just treat login = authorization, or

  • Write an AppAuthorization row; don’t overcomplicate.
  • Once done, calls onAuthorized.

Visually:

  • Small button with AI Wallet branding.
  • Text: Login with AI Wallet.

5.2 Wallet Dashboard layout (/wallet)

Sections:

  1. Header

  2. “AI Wallet”

  3. User avatar/email
  4. Link: “Try AI Wallet in a demo app”.

  5. Balance card

  6. “Current demo balance: $X.YZ”

  7. Optional: “Reset demo credits” button (calls /api/wallet/topup-demo).

  8. Activity list

  9. Table:

    • Date/Time
    • App
    • Model
    • Credits change
    • Resulting balance
    • Prompt snippet (hover for full)

That’s it. It’ll look legit with very little CSS.

5.3 Demo Chatbot layout (/try)

Sections:

  1. Top bar

  2. App name: “Demo Chatbot App”

  3. On the right:

    • LoginWithAIWalletButton
    • If logged in: “Wallet: $X.YZ demo credits”.
  4. Main

  5. Model dropdown.

  6. Messages window.
  7. Input box + “Send” button.
  8. Optional small line under messages:

    • “Last call: –$0.05 demo credits · model: gpt-4.1”.

6. Day-by-day implementation breakdown

Day 1 – Skeleton, auth, and data model

Goals:

  • Project + DB bootstrapped.
  • Auth working.
  • /wallet and /try basic pages rendering.
  • No AI or credits yet.

Tasks:

  1. Project setup

  2. Init Next.js App Router + TypeScript.

  3. Add Tailwind CSS.
  4. Set up environment vars scaffolding (.env.local).

  5. DB setup

  6. Set up Postgres (Supabase/Neon/etc.).

  7. Install Prisma.
  8. Define User, App, UsageEvent models.
  9. prisma migrate dev to create tables.
  10. Seed App with wallet-dashboard and demo-chatbot.

  11. Auth

  12. Install next-auth.

  13. Configure Google provider.
  14. On first sign-in:

    • Create User row with demoCredits = 500.
    • Build a simple <AuthGuard> for /wallet.
  15. Basic pages

  16. /wallet:

    • Protected by auth.
    • Render “Hello, [email]” from useSession().
    • Hardcode “Balance: $5.00” for now.
    • /try:

    • Public.

    • Render stub:

    • “Demo Chatbot App”

    • Placeholder Login with AI Wallet button (non-working).
    • Mock chat layout with no backend.

If you end Day 1 logged in via Google and seeing a wallet page, you’re in good shape.


Day 2 – Wallet logic, chat API, and usage logging

Goals:

  • Real credits field wired to DB.
  • /api/chat calling OpenRouter and decrementing credits.
  • Usage events being stored.
  • /wallet showing real balance and recent activity.

Tasks:

  1. Wallet APIs

  2. Implement GET /api/wallet/me:

    • Returns demoCredits and basic user info.
    • Implement GET /api/wallet/usage:

    • Fetch last N UsageEvent for user.

    • Update /wallet to fetch these via fetch/SWR/react-query.
  3. Chat API

  4. Implement POST /api/chat:

    • Validate session.
    • Parse appSlug, model, messages.
    • Look up user; get demoCredits.
    • Compute a fixed COST_PER_CALL (e.g. 5 credits).
    • If insufficient credits → return error.
    • Decrement balance, write UsageEvent, save User.demoCredits.
    • Call OpenRouter via Vercel AI SDK and stream back response.
    • For now, just log the streaming text in client.
  5. Wire chat UI

  6. On /try, build a simple chat component:

    • messages state.
    • On submit:

    • Add user message to state.

    • Call /api/chat.
    • Stream assistant tokens into messages.
    • On initial response, update wallet balance from newBalance field.
    • Handle basic “insufficient credits” case.
  7. Usage table on /wallet

  8. Map over /api/wallet/usage result.

  9. Display a simple list/table with:

    • createdAt (format locally),
    • appName,
    • model,
    • creditsDelta,
    • creditsAfter.

By end of Day 2, you should be able to:

  • Log in to wallet → see current balance & empty/seeded activity.
  • Go to /try → chat once → see balance actually changed on both /try and /wallet.

Day 3 – “Login with AI Wallet” UX, polish, YC-ready

Goals:

  • LoginWithAIWalletButton feels like an SDK.
  • Demo is stable and visually coherent.
  • Record YC video.

Tasks:

  1. Polish LoginWithAIWalletButton

  2. Create reusable component that:

    • If no session: calls signIn('google').
    • If session exists: show “Connected as [email]”.
    • Use it in /try.
    • Optionally show a tiny modal the first time:

    • “This app uses AI Wallet for login and billing.”

  3. Balance sync UX

  4. Ensure:

    • /try header displays wallet balance from /api/wallet/me.
    • After each chat call, you update balance without a full refresh (use returned newBalance or refetch).
    • On /wallet, maybe auto-refresh usage list every 5–10s or have a “Refresh” button.
  5. Optional extras (only if time allows)

  6. POST /api/wallet/topup-demo + “Reset demo credits” button.

  7. Very light RAG / FAQ:

    • Add a ? button in /try that opens a panel with Q&A about AI Wallet.
    • This can just be hard-coded text for the demo.
  8. Styling pass

  9. Make sure layouts don’t look broken:

    • Centered content, consistent padding.
    • Balance card looks like a card.
    • Activity table tidy.
    • Add minimal branding:

    • Logo text “AI Wallet” in both apps, with slightly different accent color so they feel distinct.

  10. Record YC video

  11. Script:

    1. Start on /wallet → “Here is AI Wallet. Users log in and see their balance and all AI app activity.”
    2. Click “Try AI Wallet in a demo app” → /try.
    3. Show Login with AI Wallet → confirm connected.
    4. Send a message; show streaming + balance drop.
    5. Switch back to /wallet → new activity visible.

If you stick to this spec and don’t let yourself get pulled into extra features, this is a very realistic 3-day build and tells the AI Wallet story clearly.

If you’d like, next I can help you translate this into a tiny README / architecture doc you can paste into the repo (for yourself, and later, for a cofounder/YC partner to skim).