Setup

Environment variables

All runtime configuration lives in .env.local during development and in your host's environment settings in production. .env.local.example is a minimal template; the table below is the full reference.

Required

Variable Where to find it Notes
NEXT_PUBLIC_SUPABASE_URL Supabase → Project Settings → API → Project URL Public. Shipped to the browser.
NEXT_PUBLIC_SUPABASE_ANON_KEY Supabase → Project Settings → API → anon / public key Public. Relies on RLS for safety.
SUPABASE_SERVICE_ROLE_KEY Supabase → Project Settings → API → service_role key Secret. Bypasses RLS. Used by webhook + admin routes only.
ENCRYPTION_KEY Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" 64 hex chars (32 bytes, AES-256-CBC). Rotating breaks existing tokens.
META_APP_SECRET Meta → App Settings → Basic → App Secret Verifies the X-Hub-Signature-256 HMAC on every inbound webhook. Without it the webhook rejects every request — a public deploy cannot receive messages until this is set.
Variable Purpose
NEXT_PUBLIC_SITE_URL Canonical public URL (e.g., https://crm.example.com). Used for absolute URLs, sitemap, OG images.

Embedded Signup (coexistence and multi-number onboarding)

These power Settings → WhatsApp → Números conectados. They are read at runtime — the browser asks GET /api/whatsapp/numbers for them — so each value is declared once, on the server, and changing one needs no rebuild. Without them the onboarding buttons render disabled and say so. See coexistencia-y-bandejas.md.

Variable Purpose
META_APP_ID Meta → App Settings → Basic → App ID. Used by Embedded Signup and by the Instagram/Messenger OAuth flow.
META_EMBEDDED_SIGNUP_CONFIG_ID Meta app → Facebook Login for Business → Configurations. Every onboarding button is disabled without it.
META_COEXISTENCE_CONFIG_ID Optional. Coexistence needs no configuration of its own — it is a featureType on the call — and falls back to the one above.
META_ES_VERSION Optional escape hatch, v2 or v3. A configuration created from the wizard's "Products" step is v4, and v4 ignores extras.

The NEXT_PUBLIC_-prefixed spellings of these four are still accepted as a fallback, so a deployment configured the old way keeps working.

Optional

Variable Purpose
AUTOMATION_CRON_SECRET Shared secret that protects GET /api/automations/cron. Required if you schedule the automations drain. See automations-and-cron.md.
PUBLIC_API_MAX_RANGE_DAYS Largest date range one Data API request may ask for. Default 92. See public-api.md.
PUBLIC_API_DEFAULT_RANGE_DAYS Window applied when a Data API request omits from/to. Default 30.
PUBLIC_API_EXPORT_MAX_ROWS Row ceiling for one GET /api/v1/export call. Default 50000.

The Data API itself needs no env var to switch on — its credentials are API keys created in Settings → API. It does need SUPABASE_SERVICE_ROLE_KEY (already required above); without it every /api/v1/* call returns 503.

Sample .env.local

# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://abcd1234.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...

# Meta App Secret — required for webhook signature verification
META_APP_SECRET=abcdef0123456789...

# Encryption — DO NOT change after first deploy
ENCRYPTION_KEY=3f9c0a7e4d8b2f1a6c5e8d4b9f0a2c6e8d4b9f0a2c6e8d4b9f0a2c6e8d4b9f0a

# Public URL
NEXT_PUBLIC_SITE_URL=https://crm.example.com

# Automation cron
AUTOMATION_CRON_SECRET=generate-a-long-random-string

# Embedded Signup / coexistence
META_APP_ID=1234567890
META_EMBEDDED_SIGNUP_CONFIG_ID=9876543210

Security checklist

  • Never commit .env.local. The repo already ignores it.
  • On Hostinger Managed Node.js (and any other host), set env vars via the platform's Environment variables panel rather than writing them into a tracked file on disk.
  • Rotate SUPABASE_SERVICE_ROLE_KEY if it leaks — Supabase lets you regenerate it under Project Settings → API.
  • Treat ENCRYPTION_KEY like a database master key. Losing it means connected WhatsApp® accounts must reconnect; rotating it means the same.

Next step

Deploy on Hostinger →