ShipAgent

Environment variables

Every variable in .env.example, grouped as the file groups them, with whether you need it, what it does and which skill sets it.

This is the list in .env.example, in the file's own order. Locally the values go in .env.local. In production they go in cloudflare/.env.production.local (encrypted into the repo by the deploy) or the Vercel project's Environment Variables. Never commit a key.

Only three variables are needed for the app to start and let you sign in: NEXT_PUBLIC_SITE_URL, DATABASE_URL and BETTER_AUTH_SECRET. Everything else turns a feature on.

NEXT_PUBLIC_* values are compiled into the browser code at build time. Change one and you must rebuild, not just restart.

"Set by" names the skill that asks for or writes the value; "none" means you set it by hand when you want the feature.

Site info

Variable Required What it does Set by
NEXT_PUBLIC_SITE_URL yes The site's origin, used for links in mails, OAuth callbacks, the sitemap and Better Auth's trusted origin. http://localhost:3000 locally .env.example default; deploy-cloudflare / deploy-vercel for production
NEXT_PUBLIC_PRICING_PATH no Where "buy" and "upgrade" links point. Default /#pricing none
NEXT_PUBLIC_LOCALE_DETECTION no true lets next-intl pick the locale from the browser; false keeps the URL as the only source none
NEXT_PUBLIC_OPTIMIZED_IMAGES no false turns off Next.js image optimization. Must be false on Cloudflare Workers deploy-cloudflare, deploy-vercel
NEXT_PUBLIC_LOGIN_MODE no dialog opens sign-in in a popup; page sends the user to /login none
NEXT_PUBLIC_COOKIE_CONSENT_ENABLED no true shows a cookie banner and stops signup-source tracking for visitors who decline none
NEXT_PUBLIC_USER_SOURCE_TRACKING_ENABLED no true records where each new user came from (UTM, referrer, affiliate code) in user_source none
NEXT_PUBLIC_RATE_LIMIT_ENABLED no true turns on Better Auth's per-IP limits in production (3 magic links per minute and so on) none
NEXT_PUBLIC_EMAIL_NORMALIZATION_ENABLED no true normalizes provider-specific address variants (for example Gmail dots and +tags) before signup and newsletter checks none

Database

Variable Required What it does Set by
DATABASE_URL yes Postgres connection string. On Cloudflare Workers the app connects through Hyperdrive instead, which the deploy script creates from this value setup-database

Auth (Better Auth)

Variable Required What it does Set by
BETTER_AUTH_SECRET yes Signs sessions and cookies. Generate with openssl rand -base64 32; use a different one in production setup-auth
NEXT_PUBLIC_GITHUB_CLIENT_ID no GitHub OAuth client id; the GitHub button appears only when set setup-auth
GITHUB_CLIENT_SECRET with the GitHub id GitHub OAuth client secret setup-auth
NEXT_PUBLIC_GOOGLE_CLIENT_ID no Google OAuth client id; the Google button and One Tap appear only when set setup-auth
GOOGLE_CLIENT_SECRET with the Google id Google OAuth client secret setup-auth

Cloudflare Turnstile

Variable Required What it does Set by
NEXT_PUBLIC_TURNSTILE_SITE_KEY no Turnstile widget key for the login form setup-auth
TURNSTILE_SECRET_KEY with the site key Turns on the captcha check for the three routes that send sign-in mail setup-auth

Email provider

Variable Required What it does Set by
DEFAULT_EMAIL_PROVIDER no resend (default) or cloudflare setup-email
DEFAULT_ADMIN_EMAIL for any mail The sender of every mail and the recipient of admin alerts. Its domain must be verified with the provider setup-email
DEFAULT_ADMIN_NAME no Sender name; empty means the site name from config/site.ts setup-email

Resend

Variable Required What it does Set by
RESEND_API_KEY for email with Resend Resend API key. Email sign-in, welcome and payment mails need it (or the Cloudflare pair below) setup-email

Cloudflare Email Sending

Variable Required What it does Set by
CLOUDFLARE_EMAIL_API_TOKEN with DEFAULT_EMAIL_PROVIDER=cloudflare API token with the permission Account → Email Sending → Send setup-email
CLOUDFLARE_ACCOUNT_ID with the token above The Cloudflare account that sends setup-email

Redis provider

Variable Required What it does Set by
DEFAULT_REDIS_PROVIDER no upstash (default) or ioredis for a self-hosted Redis (Node servers and Vercel only, not Workers) setup-ai

Upstash

Variable Required What it does Set by
UPSTASH_REDIS_REST_URL for video jobs, rate limits, alert dedup, the signup-gift gate Upstash REST endpoint setup-ai
UPSTASH_REDIS_REST_TOKEN with the URL Upstash REST token setup-ai

Without Redis the app still runs: rate limits allow everything, the signup-gift gate grants every gift, and alert mails are not deduplicated. Async video generation does need it.

Self-hosted Redis

Variable Required What it does Set by
REDIS_URL with DEFAULT_REDIS_PROVIDER=ioredis redis:// or rediss:// connection string setup-ai

Stripe

Variable Required What it does Set by
NEXT_PUBLIC_DEFAULT_CURRENCY no Currency used to format amounts and to total lifetime spend on the admin users page. Default usd none
STRIPE_SECRET_KEY for Stripe Secret or restricted key. sk_live_ / rk_live_ selects the plans' live price ids, anything else test setup-stripe
STRIPE_WEBHOOK_SECRET with Stripe Signing secret of the webhook endpoint (whsec_…); without it the webhook answers 400 setup-stripe (local), deploy-cloudflare / deploy-vercel (production)
STRIPE_RADAR_EARLY_FRAUD_WARNING_TYPE no What to do on a Radar early fraud warning: refund,email, refund or email; empty does nothing none

Creem

Variable Required What it does Set by
CREEM_API_BASE_URL no Override of the Creem API address. When empty the SDK picks the test or live API from the key none
CREEM_API_KEY for Creem Creem API key; a creem_test_ key selects the plans' test product ids none
CREEM_WEBHOOK_SECRET with Creem Verifies Creem webhooks; without it /api/creem/webhook answers 404 none

PayPal

Variable Required What it does Set by
NEXT_PUBLIC_ENABLE_PAYPAL for PayPal true loads the PayPal script for the pricing page buttons none
NEXT_PUBLIC_PAYPAL_CLIENT_ID for PayPal REST app client id. The server routes are on only when this and the secret are set none
NEXT_PUBLIC_PAYPAL_ENVIRONMENT for PayPal sandbox or live; selects the API and the plans' test or live plan ids none
PAYPAL_CLIENT_SECRET for PayPal REST app secret none
PAYPAL_WEBHOOK_ID for PayPal Id of the webhook in the PayPal dashboard, used to verify webhook signatures none

Cloudflare R2

Variable Required What it does Set by
R2_ACCOUNT_ID for uploads Cloudflare account that owns the bucket setup-storage
R2_ACCESS_KEY_ID for uploads R2 API token access key setup-storage
R2_SECRET_ACCESS_KEY for uploads R2 API token secret setup-storage
R2_BUCKET_NAME for uploads Bucket name setup-storage
R2_PUBLIC_URL for uploads Public address of the bucket, starting with https://; also allowed as an image source in next.config.mjs setup-storage

AI

Any one provider key is enough to run the demos. See AI.

Variable Required What it does Set by
OPENAI_API_KEY no OpenAI models setup-ai
DEEPSEEK_API_KEY no DeepSeek models setup-ai
ANTHROPIC_API_KEY no Anthropic models setup-ai
XAI_API_KEY no xAI (Grok) models setup-ai
GOOGLE_GENERATIVE_AI_API_KEY no Google Gemini models setup-ai
OPENROUTER_API_KEY no OpenRouter, many models behind one key setup-ai
FIRECRAWL_API_KEY no Firecrawl web scraping, read by lib/scraping.ts none
REPLICATE_API_TOKEN no Replicate image and video models setup-ai
REPLICATE_WEBHOOK_SIGNING_SECRET no Verifies Replicate callbacks setup-ai
FAL_KEY no fal.ai image and video models setup-ai
FAL_VERIFY_WEBHOOKS no true verifies fal.ai callbacks setup-ai
KIE_API_KEY no KIE models setup-ai
KIE_WEBHOOK_HMAC_KEY no Verifies KIE callbacks setup-ai
WEBHOOK_BASE_URL for async video Public base URL that Replicate, fal.ai and KIE call back (your domain, or a tunnel in local development) setup-ai
CUSTOM_OPENAI_BASE_URL no Base URL of any OpenAI-compatible API (SiliconFlow, 302.ai, Together AI …) setup-ai
CUSTOM_OPENAI_API_KEY with the base URL Key for that API setup-ai
NEXT_PUBLIC_CUSTOM_OPENAI_MODELS with the base URL Its models, as model-id:Display Name,model-id-2:Display Name 2 setup-ai

Translate

Variable Required What it does Set by
NEXT_PUBLIC_AI_MODEL_ID for the CMS translate button Model id used by /api/admin/translate none
NEXT_PUBLIC_AI_PROVIDER for the CMS translate button openai, deepseek, anthropic, xai or google; that provider's key must be set none

Analytics and ads

All optional; each script loads only when its value is set. One line each on Analytics.

Variable What it does
NEXT_PUBLIC_GOOGLE_ID Google Analytics 4 measurement id
NEXT_PUBLIC_GOOGLE_ADSENSE_ID AdSense publisher id (the digits after ca-pub-)
NEXT_PUBLIC_CLARITY_PROJECT_ID Microsoft Clarity project
NEXT_PUBLIC_PLAUSIBLE_SRC, NEXT_PUBLIC_PLAUSIBLE_DOMAIN Plausible script and site domain
PLAUSIBLE_API_KEY, PLAUSIBLE_URL Plausible stats API, read by the visitor-count helpers in actions/plausible
NEXT_PUBLIC_UMAMI_SRC, NEXT_PUBLIC_UMAMI_WEBSITE_ID Umami script and website id
NEXT_PUBLIC_RYBBIT_SRC, NEXT_PUBLIC_RYBBIT_SITE_ID Rybbit script and site id
NEXT_PUBLIC_RYBBIT_SESSION_REPLAY true records Rybbit session replays
NEXT_PUBLIC_RYBBIT_REPLAY_MASK_SELECTORS JSON list of CSS selectors masked in replays
NEXT_PUBLIC_POSTHOG_HOST, NEXT_PUBLIC_POSTHOG_KEY PostHog host and project key

None of these is set by a skill.

Customer support

Variable Required What it does Set by
NEXT_PUBLIC_CRISP_WEBSITE_ID no Loads the Crisp chat widget none

Discord

Variable Required What it does Set by
NEXT_PUBLIC_DISCORD_INVITE_URL no Invite link shown in the site's social links and used as the support link in payment mails none
DISCORD_WEBHOOK_URL no Channel webhook for order, pending-payment, duplicate, dispute and signup-gift notifications none

Tolt (affiliates)

Variable Required What it does Set by
NEXT_PUBLIC_TOLT_ID no Loads the Tolt script; the referral id is passed to checkout and stored in payment metadata none

Sentry

Variable Required What it does Set by
NEXT_PUBLIC_SENTRY_DSN no Turns on Sentry error reporting none
SENTRY_AUTH_TOKEN no Uploads source maps during a CI build none
SENTRY_ORG with the token Sentry organization slug none
SENTRY_PROJECT with the token Sentry project slug none
SENTRY_DEBUG no true also sends events from development none

Logging (Pino)

Variable Required What it does Set by
LOG_LEVEL no trace, debug, info (default), warn, error or fatal none
LOG_DIR no When set in production, also writes rotating log files to this folder. Leave empty on cloud hosts none

Cron

Variable Required What it does Set by
CRON_SECRET for subscriptions or PayPal Bearer secret for /api/cron/credits (yearly-plan monthly grants, renewal and PayPal reconciliation). Without it a call is refused or is only a health check deploy-cloudflare, deploy-vercel

Event tracking (optional)

Variable Required What it does Set by
TRAFFIC_SITE no Site name sent with each event. Tracking is off unless the next two are set too none
TRAFFIC_INGEST_TOKEN no Token for your event collector none
TRAFFIC_INGEST_URL no Your collector's endpoint; receives JSON POSTs none
TRAFFIC_LOG no off records nothing; on also records local runs, under <TRAFFIC_SITE>-dev none

Feedback alerts

Variable Required What it does Set by
ALERT_RESEND_KEY no Resend key used only to mail you when a user sends feedback. Separate from RESEND_API_KEY, which turns on email sign-in and welcome mail none (setup-email's check flags a half-set trio)
ALERT_FROM with the key Sender of the feedback alert none
ALERT_TO with the key Where the alert goes none

Read by the code, not in .env.example

Two optional variables are read by the code but missing from .env.example:

Variable What it does
DISCORD_ORDER_WEBHOOK_URL A separate Discord channel for order notifications; falls back to DISCORD_WEBHOOK_URL
NEXT_PUBLIC_BETTER_AUTH_URL Better Auth's base URL; falls back to NEXT_PUBLIC_SITE_URL