Deployment Guide

Vercel for the Next.js app, Supabase for prod data, Stripe live mode, and HTTPS webhook URLs that match what you configured locally: same checklist, stricter keys.

Prerequisites

  • Vercel account
  • Supabase project (production)
  • Stripe account (live mode)
  • At least one AI provider key (OpenAI and/or Anthropic)

1. Environment Variables

Set these in your Vercel project → Settings → Environment Variables.

# Supabase (Production)
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# Stripe (Live Mode)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_PRO_PRICE_ID=price_...           # Professional plan: $49/mo
STRIPE_TOPUP_PRICE_ID=price_...         # Credit top-up: $20 one-time (1000 credits)
STRIPE_ENTERPRISE_PRICE_ID=price_...    # Enterprise plan: $199/mo

# AI Providers
OPENAI_API_KEY=sk-proj-...
ANTHROPIC_API_KEY=sk-ant-...

# App URL (your production domain, no trailing slash)
NEXT_PUBLIC_APP_URL=https://your-domain.com

# Cron secret: protects /api/cron/reset-credits
CRON_SECRET=your_random_secret_here

Note: SUPABASE_SERVICE_ROLE_KEY is server-only and never exposed to the browser. Never prefix it with NEXT_PUBLIC_.

2. Vercel Setup

Connect Repository

  1. Go to vercel.com → Add New Project
  2. Import your GitHub repository
  3. Framework: Next.js (auto-detected)
  4. Build command: pnpm build
  5. Install command: pnpm install
  6. Root directory: .

Deploy

Add all environment variables then click Deploy.

The vercel.json at the root registers a daily cron job:

{
  "crons": [
    {
      "path": "/api/cron/reset-credits",
      "schedule": "0 0 * * *"
    }
  ]
}

This runs at midnight UTC and resets credits for users whose billing cycle has renewed. It requires the Authorization: Bearer <CRON_SECRET> header, which Vercel Cron sends automatically using the CRON_SECRET env var.

3. Database Setup

Run the Schema

  1. Open SQL Editor in Supabase dashboard
  2. Paste the full contents of src/db/schema.sql
  3. Click Run

This creates all tables, RLS policies, triggers, indexes, and storage policies.

Migrations for Existing Databases

If you already have a running database and are updating from an older version, run these migration statements:

-- Add display name column (if not present)
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS name text;

-- Add max_credits column: tracks the credit ceiling set at subscription/top-up time
-- This keeps the denominator stable (e.g. 3595/3600 instead of 3595/3595)
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS max_credits int;
UPDATE profiles SET max_credits = credits WHERE max_credits IS NULL;

Create Storage Bucket

  1. Go to Storage in Supabase dashboard
  2. Create bucket named uploads
  3. Set to Private
  4. Storage policies are already in schema.sql: no extra setup needed

Configure Auth Providers

  1. Go to Authentication → Providers
  2. Enable Google OAuth:
    • Create OAuth app in Google Cloud Console
    • Add callback URL: https://xxxxx.supabase.co/auth/v1/callback
    • Paste client ID and secret
  3. Enable GitHub OAuth:
    • Create OAuth app in GitHub Developer Settings
    • Add callback URL: https://xxxxx.supabase.co/auth/v1/callback
    • Paste client ID and secret

4. Stripe Setup

Create Products

In Stripe Dashboard → Products, create three products:

ProductBillingPrice
Professional PlanRecurring / Monthly$49/mo
Enterprise PlanRecurring / Monthly$199/mo
Credit Top-up (1000 credits)One-time$20

For each product, copy the Price ID (price_xxx): not the Product ID: into your environment variables.

Configure Webhook

  1. Go to Developers → Webhooks
  2. Add endpoint: https://your-domain.com/api/webhooks/stripe
  3. Select these events:
    • checkout.session.completed
    • invoice.paid
    • customer.subscription.deleted
  4. Copy the Webhook Signing Secret (whsec_xxx) into STRIPE_WEBHOOK_SECRET

Product ID vs Price ID: Stripe shows both. You need the Price ID that starts with price_, not the Product ID that starts with prod_.

5. Custom Domain

  1. Go to Vercel Project → Settings → Domains
  2. Add your domain
  3. Configure DNS as instructed by Vercel
  4. Update NEXT_PUBLIC_APP_URL to your production domain

6. Post-Deployment Checklist

Database

  • Schema SQL ran without errors
  • Migration SQL ran (if upgrading existing database)
  • Storage bucket uploads created as Private
  • Auth providers configured

Stripe

  • Three products created (Pro, Enterprise, Top-up)
  • Price IDs added to environment variables (not Product IDs)
  • Webhook endpoint registered with all three events
  • Webhook signing secret in STRIPE_WEBHOOK_SECRET

Security

  • SUPABASE_SERVICE_ROLE_KEY only in server env (no NEXT_PUBLIC_ prefix)
  • STRIPE_SECRET_KEY only in server env
  • CRON_SECRET set to a random string (e.g. openssl rand -hex 32)
  • RLS policies verified in Supabase

Functionality

  • Sign up / sign in works
  • Chat sends and receives messages
  • Credits deduct after each message
  • Document upload and RAG retrieval works
  • Stripe checkout completes and tier updates
  • Subscription webhook grants correct credits
  • Top-up webhook adds credits without resetting max
  • Monthly cron resets credits at midnight UTC

7. Monitoring

ServiceWhere to look
App errorsVercel → Deployments → Functions logs
AuthSupabase → Authentication → Logs
DatabaseSupabase → Database → Logs
WebhooksStripe → Developers → Webhooks → recent deliveries
CronVercel → Cron Jobs tab (Pro plan required for cron)

8. Scaling

Connection Pooling

Enable Supabase connection pooler (PgBouncer) for high-traffic deployments:

  • Set the connection string to the pooler URL in your Supabase project settings

Rate Limiting

The chat API has no built-in rate limiting. For production add:

  • Upstash Ratelimit (Redis-backed, Vercel Edge compatible)
  • Vercel's built-in Edge Middleware rate limiting

Edge Caching

Marketing pages use force-static with hourly revalidation and are automatically cached by Vercel's CDN: no extra configuration needed.