1. Legal and GDPR

  • Company info — fill LEGAL_COMPANY_NAME, LEGAL_ADDRESS, and LEGAL_REGISTRATION_NUMBER in .env.local (and later in your production env). They render on legal pages and in invoices.
  • Legal pages — log in as admin, open /admin-dashboard/cms, and finalize /terms, /privacy, /legal. Also review the built-in /privacy-choices page so the public rights/opt-out copy matches your actual processors and advertising setup.
  • Cookie consent — the banner is wired by category (necessary / analytics / marketing). Verify the categories match what you actually load, and test that Global Privacy Control keeps marketing/targeted-advertising consent disabled.
  • Account deletion — works out of the box (30-day grace period). Make sure the cron job process-account-deletions is enabled in /admin-dashboard/jobs.

2. Production env vars

Two categories — they live differently:

TypeExamplesWhere to setTo change later
Build-timeNEXT_PUBLIC_*, NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYSet before npm run build — they are baked into the JS bundleRequires a new build/deploy
RuntimeSTRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, SUPABASE_SECRET_KEY, OPENAI_API_KEYSet on the server / container envRestart the container; no rebuild needed

3. Deploy

Pick a deployment target:

4. Production webhooks

The Stripe CLI was only for dev. In production:

  1. Switch Stripe to live mode and copy the live keys into your production env.
  2. Open Developers → Webhooks → Add endpoint.
  3. URL: https://your-domain.com/api/stripe/webhook
  4. Subscribe to all customer.subscription.*, invoice.*, charge.*, checkout.session.*, customer.*, and payment_method.* events. At minimum: checkout.session.completed, customer.subscription.{created,updated,deleted,paused,resumed,trial_will_end}, invoice.{paid,payment_failed,payment_action_required}, charge.{refunded,dispute.created,dispute.closed}, customer.{created,updated,deleted}, payment_method.{attached,detached}, checkout.session.{async_payment_succeeded,async_payment_failed,expired}. Full list in .claude/rules/billing.md.
  5. Copy the whsec_... signing secret into STRIPE_WEBHOOK_SECRET in prod env. Restart.

5. Observability

  • Error logs — set LOGS_ENABLED=true, generate a strong LOGS_SALT, pick a LOGS_RETENTION_DAYS. Logs surface in /admin-dashboard/logs.
  • Rate limiting — create an Upstash Redis database and paste UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN. Without these, rate limits use an in-memory fallback that resets on every deploy.
  • Jobs API URL — update app_settings.jobs_api_url in the database to the production URL so cron triggers reach the right host.
  • Indexing — set NEXT_PUBLIC_INDEXABLE=true only after legal pages are real and placeholder text is gone.

6. Final smoke tests

Walk through these on the live URL before announcing the launch:

  1. Landing page renders, no console errors, branding correct
  2. GET /api/health returns 200
  3. Magic link arrives within 30 seconds in a real inbox
  4. Admin can reach /admin-dashboard
  5. Public user can complete a live checkout
  6. A chat message streams from the LLM and credits decrement
  7. Cookie banner appears for new visitors and respects choices
  8. Footer links expose /privacy, /privacy-choices, /terms, /legal, and Cookie settings
  9. Legal pages have your real company info, not placeholders
You are live when…
  • All 8 smoke tests above pass on the production URL
  • Stripe webhooks deliver successfully (check the Stripe dashboard log)
  • Scheduled jobs ran at least once (check job_runs)
  • You sent a magic link to a teammate and they got in

Where to next?

The onboarding is done. From here, dive into specific features as you need them: