For AI agents: fetch the complete documentation index at /llms.txt, the full documentation corpus at /llms-full.txt, the agent skill at /skill.md, or this page as Markdown at /docs/web/provider-keys.md (or send Accept: text/markdown).
Collect the remaining provider keys
Short walkthroughs for the services left: AI providers, Trigger.dev, S3 storage, Sentry or PostHog, Turnstile, and your domain.
How to use this page
Each section below is one complete setup walkthrough: make the account, find the key, set the env names, verify. Take only the sections for services your product ships — a site with no AI features never needs the first one, and the kit keeps running with every section here skipped.
Same secret rules as everywhere: API keys and webhook secrets stay server-only, only values prefixed NEXT_PUBLIC_/EXPO_PUBLIC_ may reach the client, production secrets live in the host's secret store, and the dev server rereads env only at restart.
AI provider keys
The kit's AI_PROVIDER accepts openai, anthropic, openai-compatible, and openrouter — plus fake, the default local stand-in. Pick one; the provider key is the only account needed, because model ids and prices come from the kit's database catalog, not env vars.
OpenAI: platform.openai.com → API keys → Create new secret key → copy sk-… → OPENAI_API_KEY. Anthropic: console.anthropic.com → API Keys → Create Key → sk-ant-… → ANTHROPIC_API_KEY. Both bill per token — a few dollars of credit covers development.
OpenRouter routes every catalog model through one key: openrouter.ai → Keys → Create Key → sk-or-… → AI_PROVIDER=openrouter plus OPENROUTER_API_KEY; optional OPENROUTER_APP_NAME/OPENROUTER_SITE_URL send attribution headers. openai-compatible covers any OpenAI-shaped endpoint: OPENAI_COMPATIBLE_API_KEY plus OPENAI_COMPATIBLE_BASE_URL.
Verify: restart, run one AI task, and expect a real model answer instead of the deterministic fake. A startup error naming the key means the provider flag is on without its key. A real reply also needs JOB_PROVIDER=trigger from the Trigger.dev section below plus one enabled model in the kit's catalog — Set up AI covers that setup.
# apps/web/.env.local — pick ONE provider
AI_PROVIDER=openai
OPENAI_API_KEY=sk-…
# or AI_PROVIDER=anthropic
# ANTHROPIC_API_KEY=sk-ant-…
# or AI_PROVIDER=openrouter
# OPENROUTER_API_KEY=sk-or-…
# or AI_PROVIDER=openai-compatible
# OPENAI_COMPATIBLE_API_KEY=…
# OPENAI_COMPATIBLE_BASE_URL=https://your-endpoint/v1Trigger.dev
Skip unless the product needs real background-job runs — JOB_PROVIDER=local queues jobs in memory without executing them, which is right for development.
1. Open app.trigger.dev → sign up → create a project.
2. Copy two values: the project ref proj_… from the project's settings or dashboard URL → TRIGGER_PROJECT_REF, and the secret key from the project's API keys page (tr_dev_… for the dev environment, tr_prod_… for production) → TRIGGER_SECRET_KEY. Both stay server-only.
3. The kit's tasks already live in apps/web/trigger and apps/web/trigger.config.ts reads TRIGGER_PROJECT_REF. From apps/web run npx trigger.dev@latest dev — it registers the tasks with your project and runs them locally against the dev key.
Verify: the dev command logs the registered tasks; queue one in the app and watch it execute in the dashboard's Runs list. Deployed environments run corepack pnpm --filter @supastack/web trigger:deploy to publish the task set.
# apps/web/.env.local
JOB_PROVIDER=trigger
TRIGGER_PROJECT_REF=proj_…
TRIGGER_SECRET_KEY=tr_dev_… # tr_prod_… in production
# run the worker alongside the app, from apps/web:
cd apps/web
npx trigger.dev@latest devS3-compatible storage (Cloudflare R2)
Skip unless the product stores files — STORAGE_PROVIDER=memory keeps uploads in-process for development.
Any S3-compatible bucket works; Cloudflare R2 is the free-tier-friendly pick. 1. Open dash.cloudflare.com → R2 Object Storage → Create bucket, name it, and keep the default location hint.
2. Back on the R2 overview open Manage R2 API Tokens → Create API token → permission Object Read & Write, scoped to that bucket → Create. Copy the Access Key ID, the Secret Access Key, and the S3 endpoint shown — https://<account-id>.r2.cloudflarestorage.com.
3. Set the env names below. STORAGE_REGION=auto is the R2 convention — the kit defaults us-east-1, which R2 also accepts. STORAGE_FORCE_PATH_STYLE already defaults on for custom endpoints, which R2 wants. Serving public files additionally needs STORAGE_PUBLIC_BASE_URL or CDN wiring; private files need nothing more.
Verify: restart and upload through the app — the object appears in the R2 bucket browser. A signature or 403 error means the token lacks object read/write on that bucket; create a fresh token scoped correctly.
# apps/web/.env.local
STORAGE_PROVIDER=s3
STORAGE_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
STORAGE_REGION=auto
STORAGE_BUCKET=your-bucket
STORAGE_ACCESS_KEY_ID=… # server-only
STORAGE_SECRET_ACCESS_KEY=… # server-only
# STORAGE_PUBLIC_BASE_URL= # optional public-URL baseSentry or PostHog (optional)
Entirely optional: every observability value is unset by default and the kit runs fine with none — add this when you want error reports and analytics in production.
Sentry for errors: sentry.io → create a project → pick the Next.js platform → the setup screen shows the DSN https://…@….ingest.sentry.io/… → SENTRY_DSN. Exceptions then reach Sentry tagged with correlation ids; the free tier is enough to start.
PostHog for product analytics instead: posthog.com → create a project → copy the project API key phc_… → NEXT_PUBLIC_POSTHOG_KEY plus NEXT_PUBLIC_POSTHOG_HOST (https://us.i.posthog.com, or your EU host). Those two are public browser values by design; SENTRY_DSN stays server-side. ALERT_WEBHOOK_URL optionally posts failure alerts to a Slack-compatible endpoint.
Verify: restart, raise a test exception (or replay Sentry's sample event), and confirm it lands in the project's Issues list.
# apps/web/.env.local — all optional
SENTRY_DSN=https://…@….ingest.sentry.io/…
NEXT_PUBLIC_POSTHOG_KEY=phc_…
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
# ALERT_WEBHOOK_URL=https://hooks.slack.com/services/…Cloudflare Turnstile
Bot protection for the public forms and credential endpoints — CAPTCHA_PROVIDER=off by default, so add it before opening sign-up to strangers. Free.
1. Open dash.cloudflare.com → Turnstile → Add widget → name it and list the hostnames: localhost for development plus your real domain → Create.
2. Copy both values: the Site Key — public by design, it renders inside the widget → NEXT_PUBLIC_TURNSTILE_SITE_KEY — and the Secret Key, which the server uses to verify → TURNSTILE_SECRET_KEY.
3. CAPTCHA_PROVIDER=turnstile switches it on; startup fails naming whichever key is missing. The captcha guide lists exactly which surfaces the widget protects.
Verify: open /auth or the contact form — the widget renders; submissions without a solved challenge are rejected. If the widget never loads, the hostname you are browsing is not on the widget's list.
# apps/web/.env.local
CAPTCHA_PROVIDER=turnstile
NEXT_PUBLIC_TURNSTILE_SITE_KEY=0x… # public by design
TURNSTILE_SECRET_KEY=0x… # server-onlyA domain of your own
One purchase at any registrar — Cloudflare Registrar, Porkbun, Namecheap all work. Nothing here copies into env; the domain shows up in the Services ID, Resend DNS, Turnstile hostnames, and OAuth return URLs you configured along the way.
Two ways to point it at the app: for Cloud Run use the domain mapping in the deploy guide — a few console clicks plus the DNS records Google lists. Or move DNS to Cloudflare nameservers (the registrar swaps two records) — optional, but it is also the tidy way to manage R2 public buckets and Turnstile hostnames in one place.
If you want to continue setup, read Set up database.