This project uses NEXT_PUBLIC_APP_URL as the single canonical app origin for server-generated URLs and Twilio webhook URL syncing.
- Local development: use
.env.local(never commit it) - Shared template: use
.env.example(no secrets) - Vercel: configure environment variables per environment (
Development,Preview,Production)
| Variable | Visibility | Required | Provider / Owner | Notes |
|---|---|---|---|---|
NEXT_PUBLIC_APP_URL |
Public (NEXT_PUBLIC_) |
Yes | Vercel | Canonical app URL. Must be a full absolute https:// URL in Vercel Production/Preview (for example https://callbackcloser.com). Used for redirects and Twilio webhook sync URLs. |
DATABASE_URL |
Server-only | Yes | Neon / Vercel | Prisma runtime connection string. Use the Neon pooled (-pooler) URL for app/serverless runtime. Include sslmode=require. |
DIRECT_DATABASE_URL |
Server-only | Yes (for Prisma migrations / deploys) | Neon / Vercel | Prisma direct connection for migrations (directUrl). Use the Neon direct (non--pooler) endpoint with sslmode=require. |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY |
Public (NEXT_PUBLIC_) |
Yes | Clerk / Vercel | Clerk frontend key. |
CLERK_SECRET_KEY |
Server-only | Yes | Clerk / Vercel | Clerk backend secret. |
NEXT_PUBLIC_CLERK_SIGN_IN_URL |
Public (NEXT_PUBLIC_) |
Optional (recommended) | Vercel | Usually /sign-in. Keeps Clerk routes explicit. |
NEXT_PUBLIC_CLERK_SIGN_UP_URL |
Public (NEXT_PUBLIC_) |
Optional (recommended) | Vercel | Usually /sign-up. Keeps Clerk routes explicit. |
ADMIN_EMAIL_ALLOWLIST |
Server-only | Optional | Vercel | Comma-separated admin email allowlist for /admin access. Keep this tightly scoped in production. |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Public (NEXT_PUBLIC_) |
Optional (future client-side Stripe usage) | Stripe / Vercel | Included in template for completeness. |
STRIPE_SECRET_KEY |
Server-only | Yes | Stripe / Vercel | Server Stripe API key. |
STRIPE_WEBHOOK_SECRET |
Server-only | Yes | Stripe / Vercel | Endpoint signing secret for /api/stripe/webhook. |
STRIPE_PRICE_STARTER |
Server-only | Yes | Stripe / Vercel | Starter plan Price ID. Also used for conversation usage-limit tier mapping. |
STRIPE_PRICE_PRO |
Server-only | Yes | Stripe / Vercel | Pro plan Price ID. Also used for conversation usage-limit tier mapping. |
TWILIO_ACCOUNT_SID |
Server-only | Yes | Twilio / Vercel | Twilio account SID. |
TWILIO_AUTH_TOKEN |
Server-only | Yes | Twilio / Vercel | Parent-account auth token used for managed subaccount provisioning, Messaging Service setup, and parent/subaccount webhook signature validation. |
TWILIO_WEBHOOK_AUTH_TOKEN |
Server-only | Optional | App-generated secret / Vercel | Shared secret used only for local/dev token-mode checks and webhook URL tooling when signature validation is disabled. Production does not rely on it for Twilio webhook auth. |
TWILIO_VALIDATE_SIGNATURE |
Server-only | Yes (production) | Vercel | Must be true in production. Twilio webhooks require valid X-Twilio-Signature verification using the correct Twilio account auth token for the request; production fails closed otherwise. |
RESEND_API_KEY |
Server-only | Optional | Resend / Vercel | Enables owner email delivery for qualified leads. Without it, email notifications are skipped while SMS and in-app alerts can still operate. |
CALLBACKCLOSER_FROM_EMAIL |
Server-only | Optional | Resend / Vercel | Verified sender address used for transactional owner emails. |
DEBUG_ENV_ENDPOINT_TOKEN |
Server-only | Optional | Vercel | Protects /api/debug/env in production. If unset, the endpoint returns 404 in production. |
PORTFOLIO_DEMO_MODE |
Server-only | Optional | Local / Vercel | Enables demo data/auth bypass mode for portfolio/demo screenshots. Keep disabled in production unless intentionally using demo mode. |
ALLOW_PRODUCTION_DEMO_MODE |
Server-only | Optional (break-glass only) | Vercel | Required only when intentionally running demo mode in production. If unset while PORTFOLIO_DEMO_MODE is enabled in production, startup is blocked. |
ENABLE_PUBLIC_MISSED_CALL_SIMULATOR |
Server-only | Optional | Vercel | Legacy/internal simulator backend flag. The current public /simulator page does not require it. |
SIMULATOR_BUSINESS_ID |
Server-only | Optional | Vercel | Legacy/internal simulator backend business ID for isolated admin/demo tooling. Never point this at a real customer business. |
ENABLE_PUBLIC_SIMULATOR_REAL_SMS |
Server-only | Optional | Vercel | Legacy/internal simulator backend SMS flag. Keep off by default. The current public /simulator page does not send real SMS. |
RATE_LIMIT_WINDOW_MS |
Server-only | Optional | Vercel | Shared rate-limit window in milliseconds. Default 60000. |
RATE_LIMIT_TWILIO_AUTH_MAX |
Server-only | Optional | Vercel | Max Twilio webhook requests per window for valid/authorized traffic. Default 240. |
RATE_LIMIT_TWILIO_UNAUTH_MAX |
Server-only | Optional | Vercel | Max Twilio webhook requests per window for unauthorized traffic. Default 40. |
RATE_LIMIT_STRIPE_AUTH_MAX |
Server-only | Optional | Vercel | Max Stripe webhook requests per window for valid-signed traffic. Default 240. |
RATE_LIMIT_STRIPE_UNAUTH_MAX |
Server-only | Optional | Vercel | Max Stripe webhook requests per window for invalid-signature traffic. Default 40. |
RATE_LIMIT_PROTECTED_API_MAX |
Server-only | Optional | Vercel | Max requests per window for protected Stripe mutation APIs (/api/stripe/checkout, /api/stripe/portal). Default 80. |
ALERT_WEBHOOK_URL |
Server-only | Optional | Vercel / Ops | If set, critical application errors are POSTed to this webhook for alert fan-out (Slack/Pager/incident gateway). |
ALERT_WEBHOOK_TOKEN |
Server-only | Optional | Vercel / Ops | Optional bearer token added to alert webhook requests as Authorization: Bearer <token>. |
ALERT_WEBHOOK_TIMEOUT_MS |
Server-only | Optional | Vercel / Ops | Timeout for alert webhook dispatch. Default 4000 ms. |
The app now validates required server env vars at runtime in production via lib/env.server.ts.
- Missing required vars throw a clear startup error with the variable names and provider hints.
NEXT_PUBLIC_APP_URLmust be a valid absolute URL and usehttps://in production.- If
NEXT_PUBLIC_APP_URLis missing or invalid, the app will try a safe fallback from Vercel system env vars (VERCEL_URL/VERCEL_PROJECT_PRODUCTION_URL) to avoid auth-page crashes, but you should still setNEXT_PUBLIC_APP_URLexplicitly. DATABASE_URLis checked for Neon compatibility (sslmode=require) when using aneon.techhost.DIRECT_DATABASE_URLis used by Prisma for direct migration connections (directUrl) and should be set in Vercel for builds/deploy workflows that run Prisma commands.STRIPE_PRICE_STARTERandSTRIPE_PRICE_PROare required in production so the app can map active subscriptions to Starter/Pro usage limits.- Twilio webhook auth behavior:
- Production:
TWILIO_VALIDATE_SIGNATURE=trueis required, token-only auth is rejected, and subaccount requests are verified with the matching Twilio account auth token - Non-production: disabling signature validation switches the app into explicit shared-token webhook auth mode for local/dev workflows
- Production:
- Demo mode safety guard:
- Production blocks startup/request handling if
PORTFOLIO_DEMO_MODEis enabled withoutALLOW_PRODUCTION_DEMO_MODE=true. - Use
ALLOW_PRODUCTION_DEMO_MODEonly as an explicit break-glass override.
- Production blocks startup/request handling if
- Rate limiting defaults are tuned to avoid blocking normal Twilio/Stripe provider traffic while still throttling abusive bursts. Tune limits only if you observe false positives in logs.
- Error reporting emits structured
app.errorlogs and, when configured, dispatches alert payloads toALERT_WEBHOOK_URL. NEXT_PUBLIC_APP_URLis the canonical value and should be set explicitly. If it is missing/invalid, the app can temporarily fall back to Vercel system env vars (VERCEL_URL/VERCEL_PROJECT_PRODUCTION_URL) to avoid auth-page crashes, but webhook/redirect behavior should still use an explicitNEXT_PUBLIC_APP_URL./adminaccess depends on eitherFOUNDER_CLERK_USER_IDorADMIN_EMAIL_ALLOWLIST; do not leave admin authorization implicit.- Owner email alerts are optional, but if you intend to advertise email delivery you must set both
RESEND_API_KEYandCALLBACKCLOSER_FROM_EMAIL. - The public
/simulatorsales demo is now self-contained and safe to render without Twilio or demo-workspace configuration. - The legacy env-backed simulator helpers are optional and should only point at a dedicated demo workspace via
SIMULATOR_BUSINESS_ID.
Use separate values for Preview and Production where appropriate.
NEXT_PUBLIC_APP_URL- Preview: your Vercel preview URL (or a preview-safe canonical URL if you use one)
- Production: your live domain (for example
https://app.example.com) - Must include
https://(a bare hostname likecallbackcloser.comwill fail validation)
DATABASE_URL(Neon pooled /-pooler)- Preview: preview/staging pooled database URL
- Production: production pooled database URL
DIRECT_DATABASE_URL(Neon direct / non--pooler)- Preview: preview/staging direct database URL for Prisma migrations
- Production: production direct database URL for Prisma migrations
- Stripe / Twilio / Clerk keys
- Prefer separate test/staging credentials for Preview
- Use live credentials only in Production
Twilio webhook syncing uses NEXT_PUBLIC_APP_URL. If you run webhook sync actions in Preview, they will point Twilio to the Preview URL. In most teams, Twilio webhook sync should be done only from a controlled environment (local with tunnel or Production) to avoid accidental webhook target changes.
CallbackCloser’s managed Twilio flow now assumes this sequence for customer onboarding:
- Create or reuse the business Twilio subaccount.
- Create or reuse the business Messaging Service.
- Assign the business number.
- Attach the number to the Messaging Service.
- Sync voice, SMS, and status webhooks.
- Track A2P readiness separately until approval is complete.
The product should not be treated as “live for customer messaging” until the business is both webhook-synced and A2P approved.
- Set
TWILIO_VALIDATE_SIGNATURE=true(required). - Keep
TWILIO_AUTH_TOKENsynced with the parent Twilio account auth token so the app can validate parent-account webhooks and resolve managed subaccount auth tokens. - Treat
TWILIO_WEBHOOK_AUTH_TOKENas optional local/dev tooling only; do not depend on it for production webhook auth. - Ensure Twilio points to the exact production URL (
NEXT_PUBLIC_APP_URL) so signature validation uses the same URL Twilio signed. - For US long-code messaging, track brand/campaign approval operationally. CallbackCloser now distinguishes infrastructure-ready from A2P-approved so pending registrations do not read as live.
- Redeploy the app (or trigger a new deployment)
- Run
npm run env:checklocally (or in CI) to confirm required variables are present - Re-run Twilio webhook sync if the production app URL changed
- Verify Stripe webhook endpoint is pointing at the correct environment URL
If sign-in/sign-up or other server-rendered pages fail in production with an error about NEXT_PUBLIC_APP_URL:
- In Vercel, open Project Settings -> Environment Variables.
- Set
NEXT_PUBLIC_APP_URLin the correct environment (Productionand/orPreview) to a full URL includinghttps://.- Example:
https://callbackcloser.com
- Example:
- Redeploy.
Notes:
- A value like
callbackcloser.com(missinghttps://) is invalid. - A stale preview URL can break redirects/webhook sync behavior; update it when needed.
- For debugging,
/api/debug/envreturns the resolved app URL source in non-production. In production, protect it by settingDEBUG_ENV_ENDPOINT_TOKENand calling/api/debug/env?token=.... VERCEL_URLandVERCEL_PROJECT_PRODUCTION_URLare Vercel system env vars (you usually do not set them manually).