Skip to content
Docs just relaunched - explore the new sidebar, OG images, and AI-ready content.
Overview

Setup

Turn on the optional integrations one at a time. Same shape per section so you never get lost.

Last updated on

7 min read

Quickstart gets the app running with three env vars. This page is where you turn on the optional integrations: OAuth providers, captcha, billing, email, storage, AI, analytics, monitoring, and abuse protection. Every card follows the same shape: a short intro, the steps to do on the provider's side, the env vars to set, and a verification step. Anything you skip stays gracefully degraded in the UI; the kit always boots.

The Setup Doctor

Run pnpm setup:doctor after editing apps/web/.env. It validates the core vars, then prints one line per optional integration showing whether it's enabled, what's missing, and how to fix it.

$ pnpm setup:doctor

Core setup

  • DATABASE_URLOK
  • NEXT_PUBLIC_APP_URLOK
  • BETTER_AUTH_SECRETOK

Optional integrations

  • Email (log)OK
  • GitHub OAuthdisabledMissing GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
  • Google OAuthOK
  • Turnstile captchaOK
  • Stripe billingOK
  • PostHog analyticsOK
  • Storage uploadsOK
  • Contact formOK
  • Abuse protectionOK

Use the doctor as your green-light check after each card below: the line you care about should read OK. See Commands And Scripts for what else the script does.

Optional Integrations

Email + password authentication ships enabled by default. Two-factor auth (TOTP + backup codes) and passkeys (WebAuthn) are wired and ready without any env vars; users opt in from /dashboard/account/security. Expand any integration below to turn it on.

GitHub logoGitHub OAuthOptionalLet users sign in with their GitHub account. Email + password keeps working alongside it.doctor:GitHub OAuth: OK

Create the OAuth app

  1. Open github.com/settings/developers -> OAuth Apps -> New OAuth App.
  2. Homepage URL: your NEXT_PUBLIC_APP_URL (e.g. http://localhost:3000).
  3. Authorization callback URL: <NEXT_PUBLIC_APP_URL>/api/auth/callback/github.
  4. Click Register application, then Generate a new client secret. Copy both the Client ID and Client Secret.

Set the env vars

VarExample
GITHUB_CLIENT_IDIv1.abc123...
GITHUB_CLIENT_SECRETghp_xyz...

Verify

pnpm setup:doctor reports GitHub OAuth: OK. Visit /auth/sign-in and the GitHub button is active (not greyed out).

For deeper coverage (callback wiring, scopes, adding a new provider), see Authentication.

Google logoGoogle OAuthOptionalLet users sign in with their Google account. Same shape as GitHub; the provider console is different.doctor:Google OAuth: OK

Create the OAuth client

  1. Open console.cloud.google.com/apis/credentials -> Create Credentials -> OAuth client ID.
  2. Application type: Web application.
  3. Authorized JavaScript origins: your NEXT_PUBLIC_APP_URL.
  4. Authorized redirect URIs: <NEXT_PUBLIC_APP_URL>/api/auth/callback/google.
  5. Click Create. Copy the Client ID and Client Secret.

Set the env vars

VarExample
GOOGLE_CLIENT_ID1234567890-abc.apps.googleusercontent.com
GOOGLE_CLIENT_SECRETGOCSPX-...

Verify

pnpm setup:doctor reports Google OAuth: OK. The Google button is active on /auth/sign-in.

For deeper coverage, see Authentication.

Cloudflare logoCloudflare TurnstileOptionalCaptcha protection on sign-up, sign-in, password reset, and the public contact form. Free for any traffic volume.doctor:Turnstile captcha: OK

Create the site widget

  1. Open dash.cloudflare.com -> Turnstile -> Add site.
  2. Widget mode: Managed (recommended).
  3. Domains: your production hostname plus localhost for local dev.
  4. Copy the Site Key (public) and Secret Key (server-only).

Set the env vars

VarExample
NEXT_PUBLIC_TURNSTILE_SITE_KEY0x4AAAAAAA...
TURNSTILE_SECRET_KEY0x4AAAAAAA...

Verify

pnpm setup:doctor reports Turnstile captcha: OK. Open /auth/sign-up and the captcha widget renders below the form.

For deeper coverage, see Security: Authentication, Sessions, And Captcha.

Stripe logoBilling (Stripe)OptionalSubscriptions, the customer portal, the dashboard pricing grid, and lifecycle emails. Stripe-hosted checkout; B2B / org-scoped.doctor:Stripe billing: OK

Create the Stripe products

  1. Sign up at stripe.com; stay in test mode for development.
  2. Create a Product called "Pro". Add two recurring Prices: monthly and yearly. Copy both price ids.
  3. In Developers -> API keys, copy your Secret key (sk_test_*).

Register the webhook endpoint

In the Stripe Dashboard -> Developers -> Webhooks -> Add endpoint:

  • URL: <NEXT_PUBLIC_APP_URL>/api/webhooks/stripe
  • Events: checkout.session.completed, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.trial_will_end, invoice.finalized, invoice.payment_action_required, invoice.payment_failed, invoice.payment_succeeded.

Copy the signing secret (whsec_*).

For local dev, install the Stripe CLI and run stripe listen --forward-to localhost:3000/api/webhooks/stripe. The CLI prints a whsec_* secret to use locally.

Set the env vars

VarExample
STRIPE_SECRET_KEYsk_test_51M...
STRIPE_WEBHOOK_SECRETwhsec_...
STRIPE_PRICE_ID_PRO_MONTHLYprice_1MxAbc...
STRIPE_PRICE_ID_PRO_YEARLYprice_1MxDef...

Verify

pnpm setup:doctor reports Stripe billing: OK. Visit /dashboard/billing; the plan grid renders with the Pro card and a working "Upgrade" button.

For deeper coverage (subscription phases, customizing plans, the webhook story), see Billing.

Email (Plunk)OptionalTransactional email. The default log mode needs zero setup and writes to .local/email-outbox/. Switch to Plunk for production.doctor:Email (plunk): OK

EMAIL_DELIVERY_MODE auto-resolves: plunk if PLUNK_API_KEY is set, noop if NODE_ENV=test, otherwise log. You usually don't need to set it explicitly.

Local development (default)

No setup needed. The kit writes every outgoing email as an .eml file to .local/email-outbox/. Open the file in your mail client to inspect it.

Production (Plunk)

  1. Sign up at useplunk.com.
  2. Add and verify your sending domain. Plunk walks you through the SPF, DKIM, and DMARC DNS records.
  3. Create an API key from Settings -> API.

Set the env vars (production)

VarExample
PLUNK_API_KEYsk_pk_...
EMAIL_DELIVERY_MODEplunk (auto-resolves; only set explicitly to override)
CONTACT_FORM_TO_EMAILhello@your-domain.com (only if the public contact form is enabled)

Verify

pnpm setup:doctor reports Email (plunk): OK. Trigger a transactional email (sign up a new user; the verification email goes through Plunk's outbound API). The contact form additionally requires CONTACT_FORM_TO_EMAIL plus Turnstile.

For deeper coverage (delivery modes, React Email templates, swapping providers), see Email.

AWS logoStorageOptionalS3-compatible object storage for avatars, organization logos, and chat image attachments. Pick a provider; the env-var skeleton is the same.doctor:Storage uploads: OK

Native AWS: no custom endpoint, region required. Path-style stays off (default).

AWS_REGION="us-east-1"
AWS_ENDPOINT_URL_S3=""
AWS_S3_FORCE_PATH_STYLE="false"
AWS_ACCESS_KEY_ID="<your-access-key-id>"
AWS_SECRET_ACCESS_KEY="<your-secret-access-key>"

NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES="your-bucket-name"
NEXT_PUBLIC_S3_PUBLIC_URL="https://your-bucket.s3.us-east-1.amazonaws.com"

R2 uses an account-scoped endpoint and the magic region auto. Free egress is the usual reason buyers pick it.

AWS_REGION="auto"
AWS_ENDPOINT_URL_S3="https://<account-id>.r2.cloudflarestorage.com"
AWS_S3_FORCE_PATH_STYLE="false"
AWS_ACCESS_KEY_ID="<your-r2-access-key-id>"
AWS_SECRET_ACCESS_KEY="<your-r2-secret-access-key>"

NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES="your-bucket-name"
NEXT_PUBLIC_S3_PUBLIC_URL="https://pub-<bucket-id>.r2.dev"

MinIO and most other self-hosted S3 servers expect path-style URLs. Endpoint is your MinIO host.

AWS_REGION="auto"
AWS_ENDPOINT_URL_S3="https://minio.your-domain.com"
AWS_S3_FORCE_PATH_STYLE="true"
AWS_ACCESS_KEY_ID="<your-minio-access-key>"
AWS_SECRET_ACCESS_KEY="<your-minio-secret-key>"

NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES="your-bucket-name"
NEXT_PUBLIC_S3_PUBLIC_URL="https://minio.your-domain.com/your-bucket-name"

Don't forget the bucket-side CORS rule (allow PUT from your app origin) and a lifecycle rule expiring tmp/ after 1 day. Without CORS, every upload fails silently. Both rules are copy-pasteable in Storage required bucket configuration.

Verify. pnpm setup:doctor reports Storage uploads: OK. Visit /dashboard/account and upload an avatar; it persists and shows up immediately on the navbar.

For deeper coverage (the presign + finalize pipeline, image validation, what gets stored where), see Storage.

Vercel logoAI (Vercel AI Gateway)OptionalOne key unlocks every supported provider through gateway(modelId). No per-provider keys, no swapping SDKs when a new model lands.

AI_GATEWAY_API_KEY is not yet in apps/web/.env.example. Add it to your .env manually. pnpm setup:doctor doesn't currently track the AI Gateway either; verify by sending a chat message instead.

Create a gateway key

  1. Sign in at vercel.com.
  2. Open the AI Gateway page from your dashboard.
  3. Create a new API key. Copy the value.

Set the env var

VarExample
AI_GATEWAY_API_KEYvck_...

Verify

Visit /dashboard/ai-chat, send a message, and confirm tokens stream back. If the request fails at the SDK boundary with an authentication error, the key isn't being read; restart the dev server after editing .env.

For deeper coverage (the streaming chat handler, the ai-elements building blocks, billing gates per model), see AI.

PostHog logoAnalytics (PostHog)OptionalProduct analytics, web analytics, session replay, and the foundation that Monitoring builds on.doctor:PostHog analytics: OK

Create a PostHog project

  1. Sign up at posthog.com.
  2. Create a project; copy the Project API Key from Project Settings.
  3. Note your API host (e.g. https://us.i.posthog.com) and UI host (e.g. https://us.posthog.com). The exact values depend on which region you picked at signup; PostHog shows them on the same settings page.

Set the env vars

VarExample
NEXT_PUBLIC_POSTHOG_KEYphc_...
NEXT_PUBLIC_POSTHOG_HOSThttps://us.i.posthog.com
NEXT_PUBLIC_POSTHOG_UI_HOSThttps://us.posthog.com
POSTHOG_PROXY_INGEST_HOSToptional; same as NEXT_PUBLIC_POSTHOG_HOST to enable the same-origin reverse proxy
POSTHOG_PROXY_ASSET_HOSToptional; pair with POSTHOG_PROXY_INGEST_HOST

Verify

pnpm setup:doctor reports PostHog analytics: OK. Trigger an action in the app; watch it appear in PostHog -> Live Events within seconds.

For deeper coverage (typed events, identification, session replay, the reverse-proxy story), see Analytics.

PostHog logoMonitoring (PostHog Source Maps and Logs)OptionalBuilds on Analytics. Adds de-minified stack traces in PostHog's Errors view and OpenTelemetry-shaped server logs.

Prerequisite: Analytics enabled. The two env vars below are read at build time only, so changing them requires a fresh build on every host. Set them in your build environment (Vercel, Fly, Render, Docker), not just .env.

Create a personal API key

  1. In PostHog -> Settings -> Personal API Keys -> Create personal API key.
  2. Scope: Project: write. Copy the key (phx_...).
  3. Note your numeric Project ID from Project Settings (e.g. 12345).

Set the env vars

VarExample
POSTHOG_API_KEYphx_...
POSTHOG_PROJECT_ID12345

Verify

Run pnpm build. The build log includes a "Source maps uploaded" line from withPostHogConfig. Trigger an unhandled error in the app; PostHog -> Errors shows the issue with a de-minified stack trace.

For deeper coverage (the three pipelines, structured logging via OTLP, the reverse proxy), see Monitoring.

Upstash logoAbuse Protection (Upstash Redis)Required for productionSliding-window rate limits on auth flows, the contact form, AI chat, and uploads. Required before going to production.doctor:Abuse protection: OK

Create an Upstash Redis database

  1. Sign up at upstash.com.
  2. Create a Redis database. Any region is fine; the kit uses the REST API so latency is bounded by HTTPS, not Redis protocol.
  3. Open the database -> REST API panel. Copy the REST URL and REST Token.

Set the env vars

VarExample
UPSTASH_REDIS_REST_URLhttps://us1-active-xxx.upstash.io
UPSTASH_REDIS_REST_TOKENAX...

Verify

pnpm setup:doctor reports Abuse protection: OK. Send 10 sign-in requests in a row from the same IP; the eleventh returns Too Many Requests.

For deeper coverage (per-surface limits, fail-open vs fail-closed policy, Redis tuning), see Security: Abuse Protection (Upstash).

Where To Go Next

Was this page helpful?

On this page