Individual Developer's AI SaaS Journey: Production Launch in 3 Days
9 min read

Individual Developer's AI SaaS Journey: Production Launch in 3 Days

Zero to a live B2B AI OCR product in three days with SvelteKit, Supabase, and Gemini: stack choices, what broke, and the first-quarter KPIs a solo dev set.

Three Days, One Production Launch

Over the past three days I took an AI-powered B2B SaaS from nothing to a live production service. It’s called Agent Effi Flow. The product ships two AI automation tools, Receipt OCR for Tax Refund and Accounting OCR, and it targets Japanese inbound tourism businesses along with SME accounting teams.

What follows isn’t a “this is theoretically possible” write-up. It’s what I actually built. I’ll be honest about the stack I picked and why, how the core pieces fit together, where I got stuck, and the KPI targets I set for the first three months.

Why I Built This SaaS

Problem Definition

B2B automation needs discovered while working in Japan:

  1. Manual-dependent tax refund processing: Visual passport and tax refund document verification with manual data entry
  2. Repetitive accounting tasks: Manual data cleanup after receipt OCR. AI accounting automation by the numbers shows just how significant this problem is with real data.
  3. Limitations of existing solutions: Either expensive enterprise solutions or low-accuracy generic OCR

The differentiation point as an individual developer: Structured data extraction using AI. Beyond simple text OCR, using Google Gemini API’s Structured Output feature to receive type-safe JSON responses immediately usable in business logic. For where the service headed next, see Agent Effi Flow’s pivot decision and the Omotenashi bot concept.

Technology Stack Choices

Why SvelteKit Over Next.js

Reasons for choosing SvelteKit instead of Next.js:

1. Svelte 5’s Innovative Reactivity System

// Svelte 5 Runes: $state and $derived
let count = $state(0);
let doubled = $derived(count * 2);

// More intuitive than React hooks, less boilerplate

2. Bundle Size and Performance

  • Svelte removes framework code at compile time
  • Client bundle 40% smaller than React
  • Noticeably faster Time to Interactive

3. Developer Experience

  • Low learning curve, readable code
  • Excellent TypeScript support
  • Fast HMR with Vite

Why Supabase for Backend

Supabase instead of Firebase:

1. PostgreSQL Power

-- Multi-tenant implementation with Row Level Security
CREATE POLICY "Users can only access their own data"
ON credits FOR SELECT
USING (auth.uid() = user_id);

2. Integrated Features

  • Auth: Email/social login ready to use
  • Database: PostgreSQL with real-time subscriptions
  • Storage: File upload and CDN
  • Edge Functions: Serverless functions (Deno-based)

3. Open Source and Pricing

  • Fully open source (self-hosting possible)
  • Generous free tier (50,000 MAU, 500MB DB)
  • No vendor lock-in concerns

Why Google Gemini API for AI

Gemini instead of OpenAI GPT:

1. Cost Efficiency

Gemini 2.5 Flash:
- Input: $0.075 / 1M tokens
- Output: $0.30 / 1M tokens

GPT-4 Turbo:
- Input: $10 / 1M tokens
- Output: $30 / 1M tokens

→ ~100x cheaper

2. Structured Output Support

const responseSchema = {
  type: Type.OBJECT,
  required: ['store_name', 'items', 'tax', 'total_with_tax'],
  properties: {
    store_name: { type: Type.STRING },
    items: {
      type: Type.ARRAY,
      items: {
        type: Type.OBJECT,
        properties: {
          name: { type: Type.STRING },
          quantity: { type: Type.NUMBER },
          unit_price: { type: Type.NUMBER }
        }
      }
    },
    tax: { type: Type.NUMBER },
    total_with_tax: { type: Type.NUMBER }
  }
};

// Force response to comply with schema
const result = await model.generateContent({
  contents: [{ role: 'user', parts: [{ text: prompt }] }],
  generationConfig: {
    responseMimeType: 'application/json',
    responseSchema
  }
});

3. Multimodal Performance

  • Excellent image OCR quality (OmniDocBench benchmark #1)
  • Strong at handling complex layouts like passports and receipts

Why Vercel for Deployment

1. SvelteKit Optimization

  • Direct support from Vercel, creators of SvelteKit
  • Automatic SSR/Edge Function deployment
  • Fast deployment with build caching

2. Serverless Architecture

  • API routes automatically deploy as serverless functions
  • Usage-based billing (minimal cost with no traffic)
  • Global Edge Network

Core Implementation

1. OCR API with Structured Output

Actual code (src/routes/api/receipt-ocr/+server.ts):

import { GoogleGenerativeAI, SchemaType as Type } from '@google/generative-ai';

const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
const model = genAI.getGenerativeModel({
  model: 'gemini-2.0-flash-exp'
});

// Ensure type safety with schema definition
const responseSchema = {
  type: Type.OBJECT,
  required: ['store_name', 'purchase_date', 'items', 'tax', 'total_with_tax'],
  properties: {
    store_name: {
      type: Type.STRING,
      description: 'Store name'
    },
    purchase_date: {
      type: Type.STRING,
      description: 'Purchase date in YYYY-MM-DD format'
    },
    items: {
      type: Type.ARRAY,
      items: {
        type: Type.OBJECT,
        properties: {
          name: { type: Type.STRING },
          quantity: { type: Type.NUMBER },
          unit_price: { type: Type.NUMBER },
          total_price: { type: Type.NUMBER }
        }
      }
    },
    subtotal: { type: Type.NUMBER },
    tax: { type: Type.NUMBER },
    total_with_tax: { type: Type.NUMBER }
  }
};

// Encode image to base64
const imageBase64 = await fileToBase64(imageFile);

const result = await model.generateContent({
  contents: [
    {
      role: 'user',
      parts: [
        {
          inlineData: {
            mimeType: imageFile.type,
            data: imageBase64
          }
        },
        {
          text: `Analyze the following receipt image and return structured JSON.

          Requirements:
          1. Extract store name, purchase date, item list
          2. For each item: name, quantity, unit price, total
          3. Tax and final total
          4. Convert date to YYYY-MM-DD format
          `
        }
      ]
    }
  ],
  generationConfig: {
    responseMimeType: 'application/json',
    responseSchema
  }
});

const parsedData = JSON.parse(result.response.text());
// parsedData is already a type-safe object complying with schema

Key advantages:

  • Type safety: Schema enforces response structure
  • No parsing errors: JSON parsing failures almost eliminated
  • Immediately usable: Direct use in DB storage or API responses

2. Credit System

Stripe Checkout Integration:

// src/routes/agents/credits/+page.server.ts
import Stripe from 'stripe';

const stripe = new Stripe(STRIPE_SECRET_KEY);

export const actions = {
  purchase: async ({ request, locals }) => {
    const data = await request.formData();
    const planId = data.get('plan_id');

    const plans = {
      starter: { price: 2000, credits: 1000 },
      pro: { price: 10000, credits: 5500 },
      business: { price: 40000, credits: 23000 }
    };

    const selectedPlan = plans[planId];

    // Create Stripe Checkout session
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [
        {
          price_data: {
            currency: 'jpy',
            product_data: {
              name: `${planId.toUpperCase()} Plan - ${selectedPlan.credits} Credits`
            },
            unit_amount: selectedPlan.price
          },
          quantity: 1
        }
      ],
      mode: 'payment',
      success_url: `${url.origin}/credits/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${url.origin}/credits`,
      metadata: {
        user_id: locals.user.id,
        credits: selectedPlan.credits,
        plan_id: planId
      }
    });

    return { session_url: session.url };
  }
};

Credit Grant via Webhook:

// src/routes/api/webhooks/stripe/+server.ts
export const POST = async ({ request }) => {
  const signature = request.headers.get('stripe-signature');
  const body = await request.text();

  // Verify Stripe webhook
  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    STRIPE_WEBHOOK_SECRET
  );

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    const { user_id, credits } = session.metadata;

    // Add credits to Supabase
    await supabase
      .from('credits')
      .insert({
        user_id,
        amount: parseInt(credits),
        type: 'purchase',
        description: `Purchased ${credits} credits`
      });
  }

  return new Response(JSON.stringify({ received: true }));
};

3. API Authentication

API Key Issuance and Validation:

// API key creation
export const actions = {
  createApiKey: async ({ locals }) => {
    const apiKey = `effi_${crypto.randomUUID()}`;

    await supabase
      .from('api_keys')
      .insert({
        user_id: locals.user.id,
        key: apiKey,
        created_at: new Date().toISOString()
      });

    return { apiKey };
  }
};

// API key validation (executed for all API requests)
async function validateApiKey(request: Request) {
  const apiKey = request.headers.get('X-API-Key');

  if (!apiKey) {
    throw error(401, 'API key required');
  }

  const { data: keyData } = await supabase
    .from('api_keys')
    .select('user_id, is_active')
    .eq('key', apiKey)
    .single();

  if (!keyData || !keyData.is_active) {
    throw error(401, 'Invalid or inactive API key');
  }

  return keyData.user_id;
}

// Check balance before deducting credits
async function checkAndDeductCredits(userId: string, amount: number) {
  const { data: balance } = await supabase
    .rpc('get_credit_balance', { user_id: userId });

  if (balance < amount) {
    throw error(402, 'Insufficient credits');
  }

  await supabase
    .from('credits')
    .insert({
      user_id: userId,
      amount: -amount,
      type: 'usage',
      description: 'OCR API call'
    });
}

4. Multi-language Support

i18n with Paraglide:

// src/lib/i18n.ts
import { paraglide } from '@inlang/paraglide-sveltekit';

export const i18n = paraglide({
  project: './project.inlang',
  outdir: './src/paraglide',
  defaultLocale: 'ja'
});

// Supported languages: ko, en, ja, zh, es

Language-based Routing:

// src/routes/[lang]/+layout.server.ts
export const load = async ({ params }) => {
  const lang = params.lang || 'ja';

  return {
    lang,
    translations: await import(`../../locales/${lang}.json`)
  };
};

Token Usage Tracking and Cost Optimization

Real-time Token Monitoring:

const result = await model.generateContent({...});

// Gemini API returns token usage
const usage = result.response.usageMetadata;

await supabase
  .from('api_usage')
  .insert({
    user_id,
    input_tokens: usage.promptTokenCount,
    output_tokens: usage.candidatesTokenCount,
    total_tokens: usage.totalTokenCount,
    estimated_cost: calculateCost(usage)
  });

function calculateCost(usage) {
  const INPUT_COST = 0.075 / 1_000_000; // $0.075 per 1M tokens
  const OUTPUT_COST = 0.30 / 1_000_000; // $0.30 per 1M tokens

  return (
    usage.promptTokenCount * INPUT_COST +
    usage.candidatesTokenCount * OUTPUT_COST
  );
}

Business Strategy

Target Customers

  1. Japanese Inbound Tourism Businesses

    • Duty-free shops, drugstores, department stores
    • 1,000+ tax refund processes per month
  2. SME Accounting Teams

    • 500+ receipt processing per month
    • Accounting software integration needs
  3. Tax Accountant Offices

    • Accounting outsourcing for client companies
    • Need for accurate data

SEO/AEO-Driven Acquisition

Content Marketing Strategy:

  1. Leverage jangwook.net Blog

    • Build brand credibility through technical blog
    • Target OCR and AI automation keywords
    • This article is part of that strategy
  2. Japanese Keyword Optimization

    • “免税 OCR” (tax refund OCR)
    • “経理 自動化” (accounting automation)
    • “領収書 AI” (receipt AI)
  3. FAQ Page for AEO

    • “What’s the OCR accuracy?” → Structured answer
    • “How to integrate API?” → Developer docs
    • “What’s the pricing?” → Transparent pricing table

3-Month KPI Targets

Realistic Solo Developer Goals:

MetricTargetMeasurement
Monthly Visitors500+Google Analytics
Signups30Supabase Auth table
Paid Conversions5Stripe Dashboard
MRR¥30,000Stripe subscriptions
OCR API Calls1,000api_usage table

Why These Numbers?

  • 500 visitors → Realistic with SEO
  • 6% conversion (30/500) → B2B SaaS average
  • 16.7% paid conversion (5/30) → Premium targeting
  • ¥6,000 ARPU → Fits SME budgets

Development Timeline

Day 1 (2025-11-24): Foundation

Completed:

  • Project initialization (SvelteKit + TypeScript)
  • Supabase integration (Auth + Database)
  • First service: Receipt OCR for Tax Refund
  • Passport + tax refund document auto-recognition
  • Structured Output schema validation

Code written: ~800 lines

Day 2 (2025-11-25): Payment & Second Service

Completed:

  • Accounting OCR service added
  • Stripe Checkout integration
  • Credit system implementation
  • Auto-charge via webhooks
  • Legal pages
    • Tokushoho (特定商取引法)
    • Privacy Policy
    • Terms of Service
  • Google Analytics integration

Code written: ~1,200 lines

Day 3 (2025-11-26): Polish & Launch

Completed:

  • Service description pages
  • API documentation
  • Landing page optimization
  • Production deployment (Vercel)
  • DNS setup

Code written: ~600 lines

Total: 3 days, ~2,600 lines of code

Lessons Learned

1. SvelteKit 5 Reactivity is a Game Changer

Before (React hooks):

const [credits, setCredits] = useState(0);

useEffect(() => {
  const doubled = credits * 2;
  setDoubled(doubled);
}, [credits]);

After (Svelte 5 runes):

let credits = $state(0);
let doubled = $derived(credits * 2);
// Automatic reactivity, no useEffect needed

Realized benefits:

  • 70% reduction in boilerplate code
  • Easier debugging (no explicit dependencies)
  • Performance improvement (no unnecessary re-renders)

2. Supabase RLS Makes Multi-tenant Easy

Row Level Security Policies:

-- Each user can only view their own credits
CREATE POLICY "Users can view own credits"
ON credits FOR SELECT
USING (auth.uid() = user_id);

-- Credit deduction only from server
CREATE POLICY "Only service role can deduct"
ON credits FOR INSERT
USING (auth.role() = 'service_role');

Benefits:

  • No permission checks needed in application code
  • Data isolation guaranteed at SQL level
  • Security vulnerabilities fundamentally blocked

3. Gemini API Cost Optimization

Prompt Optimization:

// Before: Long prompt
const prompt = `
You are a professional OCR system.
Analyze the following image...
(500+ character instructions)
`;
// → Input tokens: ~150

// After: Concise prompt
const prompt = `Extract receipt data as JSON:
- store_name, date, items[], tax, total`;
// → Input tokens: ~25

// Cost savings: 83%

Image Size Optimization:

// Resize images to max 1024px
import sharp from 'sharp';

const optimized = await sharp(imageBuffer)
  .resize(1024, 1024, { fit: 'inside' })
  .png({ quality: 80 })
  .toBuffer();

// Token usage: 70% reduction

4. Solo Developer Productivity Tips

Methods to maximize productivity:

  1. Local Development with Supabase CLI

    supabase start
    # Local PostgreSQL + Auth + Storage
    # Same environment as production
  2. Boilerplate Auto-generation with Claude Code

    • CRUD API scaffolding
    • TypeScript type definitions
    • Zod schema validation

    The broader approach of using AI tools to grow a side project to company scale is covered in Effloow: from side project to AI company. The hands-on details of chaining several agents to automate the work live in my notes on improving multi-agent orchestration.

  3. Vercel Preview Deployments

    • Automatic deployment URL per PR
    • Instant demo for clients

Next Steps

Immediate (Within 1 Week)

  1. Japanese Keyword SEO Optimization

    • Write meta descriptions in Japanese
    • Optimize Open Graph images
    • Add Schema.org markup
  2. Create FAQ Page

    • “What’s the OCR accuracy?” → Answer with real data
    • “What receipt formats supported?” → Sample images
    • “What are the API limits?” → Specify rate limits
  3. Submit to Google Search Console

    • Register sitemap
    • Request indexing

Short-term (Within 1 Month)

  1. Add New Services

    • Expand business document processing
  2. Acquire First B2B Customer

    • Target: 1 duty-free shop contract
    • Credit-based pricing model
  3. Enhance API Documentation

    • Write OpenAPI spec
    • Provide Postman Collection
    • SDK sample code (Python, Node.js)

Mid-term (Within 3 Months)

  1. Achieve ¥30,000 MRR

    • 5 paid customers
    • 500 API calls/month
  2. Share Use Cases

    • Customer success story blog posts
    • Document real-world implementation cases
  3. Add Premium Features

    • Batch processing
    • Custom schema support
    • Webhook integration

When to Use This Stack and When to Avoid It

The 3-day launch worked because the problem and the stack lined up well. The same choices are not the right answer for every project.

When this approach fits well:

  • A solo developer or small team validating an MVP fast. A managed backend (Supabase) and serverless deployment (Vercel) remove almost all of the infrastructure overhead.
  • Usage-based services where AI inference cost flows straight into your unit economics. Gemini 2.5 Flash’s cheap token pricing protects your margin. For a realistic breakdown of AI operating costs, see The Reality of AI Agent Costs.
  • Services with spiky or near-zero early traffic. Serverless billing drops to roughly zero when idle.
  • Cases where structured data extraction is the core value. Structured Output removes the entire parsing layer.

When to avoid or rethink it:

  • Strict data-governance or on-premise requirements. Sending images to an external LLM API can hit compliance walls on its own.
  • Latency-sensitive workloads where milliseconds matter. LLM inference takes hundreds of milliseconds to several seconds, so it is a poor fit for real-time processing.
  • Steady, predictable high-volume processing in the millions of calls per month. At that scale, dedicated infrastructure or a self-hosted model beats serverless and per-call APIs on unit cost. For a cost-and-impact analysis grounded in real operational data, see Effi Flow Automation Analysis.
  • High-stakes domains where an OCR error becomes an incident (medical prescriptions, original legal contracts). You have to keep a human review step, which breaks the “fully automated” premise.

The cost of learning a new tool is not trivial either. A team already fluent in React/Next.js may find that the learning cost of switching to SvelteKit outweighs the bundle-size savings. For the actual patterns of a solo developer raising productivity with AI tools, I wrote them up in Claude Code Usage Analysis.

References

Official Documentation

Key Articles Referenced

What the Three-Day Launch Taught Me

Launching a production SaaS in 3 days is possible. But “build fast” was never the point. The point was “validate fast.”

Three things made it work:

  1. Right tool selection: SvelteKit + Supabase + Gemini API
  2. Limited scope: Start with 2 services, prioritize launch over perfection
  3. Business first: Focus on solving customer problems over technology

The real journey starts now. The first paid customer, then the feedback, then the long grind of fixing what they tell me is broken. I hope that when I update this article in three months, I get to write “¥30,000 MRR achieved.”

Fellow solo developers, let’s build together.

Frequently Asked Questions

Why Google Gemini API instead of OpenAI GPT?
Cost was the main reason. Gemini 2.5 Flash runs at 0.075 dollars per 1M input tokens and 0.30 dollars output, roughly 100x cheaper than GPT-4 Turbo. It also supports Structured Output to enforce type-safe JSON, and its image OCR quality is strong on complex layouts like receipts and passports.
Why choose SvelteKit over Next.js?
Svelte 5's Runes reactivity is more intuitive than React hooks and cut boilerplate by about 70 percent. Because Svelte strips framework code at compile time, the client bundle is 40 percent smaller than React with faster Time to Interactive. Vercel, the deployment platform, also supports SvelteKit directly.
How was the Gemini API cost reduced?
Two methods. Shortening the long instruction prompt into a concise form dropped input tokens from about 150 to 25, a roughly 83 percent cost saving. Resizing images to within 1024px also reduced token usage by 70 percent.
What are the 3-month KPI targets?
The targets are 500+ monthly visitors, 30 signups, 5 paid conversions, 30,000 yen MRR, and 1,000 OCR API calls. The 6 percent signup rate and 16.7 percent paid conversion are realistic solo-developer figures based on B2B SaaS averages.

Read in Other Languages

Was this helpful?

Your support helps me create better content. Buy me a coffee.

About the Author

jw

Kim Jangwook

Full-Stack Developer specializing in AI/LLM

Building AI agent systems, LLM applications, and automation solutions with 10+ years of web development experience. Sharing practical insights on Claude Code, MCP, and RAG systems.

Back to Blog