Live demo of the Whirlwind template, as shippedStripe test mode: card 4242 4242 4242 4242, any future date, any CVCGet Whirlwind →

Environment Variables

Every .env key the template expects and where to find the values.

Where they live

Everything goes in .env.local, which is gitignored. npm run setup writes it for you. npm run doctor reports which keys are missing and which services fail to answer, without printing values.

Supabase

NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ...
💡

The service role key is used only on the server. Nothing secret is ever prefixed NEXT_PUBLIC_.

Stripe

STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PRICE_ID=price_...            # default plan
NEXT_PUBLIC_STRIPE_PRICE_ID_ADVANCED=price_...   # optional second plan
STRIPE_ALLOWED_PRICE_IDS=price_...,price_...     # checkout rejects anything else

AI providers

ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GOOGLE_AI_API_KEY=...

# optional model overrides
ANTHROPIC_MODEL=
OPENAI_MODEL=
GEMINI_MODEL=
💡

One provider is enough. ImageStudio needs OPENAI_API_KEY because it uses gpt-image-1 (override with OPENAI_IMAGE_MODEL).

App

NEXT_PUBLIC_APP_URL=https://yourapp.com
NEXT_PUBLIC_APP_DOMAIN=yourapp.com
NEXT_PUBLIC_SUPPORT_EMAIL=hello@yourapp.com

Optional services

# Newsletter (Mailchimp)
MAILCHIMP_API_KEY=
MAILCHIMP_AUDIENCE_ID=
MAILCHIMP_WEBHOOK_SECRET=

# Live chat (Crisp)
NEXT_PUBLIC_CRISP_ID=

# Analytics (Plausible)
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=

# Rate limiting across serverless instances (Upstash Redis)
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
💡

All optional. Magic links and confirmations are sent by Supabase Auth. Lifecycle emails (welcome, low credits) go through Resend and are skipped while RESEND_API_KEY is unset. Set Upstash before launch: the in-memory limiter only holds within one process.