Skip to main content
Sintesis uses a .env.local file for local development. Copy the example file to get started:
cp env.example .env.local
Never commit .env.local to version control. It is already listed in .gitignore. Do not expose SUPABASE_SERVICE_ROLE_KEY or any other server-only secret to the browser.

Supabase

These variables connect the app to your Supabase project. The NEXT_PUBLIC_ prefixed variables are safe to expose to the browser; the service role keys must remain server-only.
NEXT_PUBLIC_SUPABASE_URL
string
required
The base URL of your Supabase project. For local development this is http://localhost:54321. For production, find it in your Supabase dashboard under Project Settings → API → Project URL.
NEXT_PUBLIC_SUPABASE_ANON_KEY
string
required
The anonymous (public) JWT for your Supabase project. Safe to expose to the browser — Row Level Security enforces data access. Find it in Project Settings → API → Project API Keys → anon public.
SUPABASE_SERVICE_ROLE_KEY
string
The service role JWT that bypasses RLS. Used only in server-side routes and background jobs. Never expose this to the browser. Find it in Project Settings → API → Project API Keys → service_role secret.
SUPABASE_SERVICE_ROLE_KEY_V1
string
Previous version of the service role key, retained for rolling-key rotation. Leave blank unless actively rotating keys.
SUPABASE_SERVICE_ROLE_KEY_V2
string
Current version of the service role key used during key rotation. Leave blank unless actively rotating keys.
SUPABASE_SERVICE_ROLE_KEY_VERSION
string
Indicates which version slot (v1 or v2) is currently active during key rotation. Leave blank unless using the rotation mechanism.
For local development the NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY values in env.example are pre-filled with the default local Supabase stack values — no changes needed unless you customise your local ports.

Google OAuth

Required only if you enable Google sign-in. These are configured inside supabase/config.toml under [auth.external.google]not in .env.local. The Supabase CLI reads them from the config file via the env(...) syntax:
supabase/config.toml
[auth.external.google]
enabled = true
client_id = "env(GOOGLE_CLIENT_ID)"
secret = "env(GOOGLE_CLIENT_SECRET)"
redirect_uri = ""
Add the following to your shell environment or a .env file that the Supabase CLI can read:
GOOGLE_CLIENT_ID
string
OAuth 2.0 client ID from the Google Cloud Console. Create an OAuth client ID of type Web application and copy the client ID.
GOOGLE_CLIENT_SECRET
string
OAuth 2.0 client secret from the same Google Cloud credential entry. Keep this server-side only.
For local development, add http://127.0.0.1:54321/auth/v1/callback as an authorised redirect URI in your Google Cloud OAuth credential. These variables are consumed by the Supabase local stack only — they are not read by the Next.js server.

Email — Resend

Sintesis sends transactional email (invitations, notifications) via Resend.
RESEND_API_KEY
string
Your Resend API key. Create one in the Resend dashboard under API Keys. The key starts with re_.
RESEND_FROM_EMAIL
string
The verified sender address used in outgoing emails, e.g. [email protected]. The domain must be verified in your Resend account.
RESEND_API_KEY_V1
string
Previous Resend API key slot for rolling-key rotation.
RESEND_API_KEY_V2
string
Current Resend API key slot during rotation.
RESEND_API_KEY_VERSION
string
Active version slot (v1 or v2) during key rotation.

AI providers

OPENAI_API_KEY
string
OpenAI API key used for AI-assisted features such as OCR post-processing and document analysis. Create one in the OpenAI Platform under API Keys. The key starts with sk-.
OPENAI_API_KEY_V1
string
Previous OpenAI key slot for rolling-key rotation.
OPENAI_API_KEY_V2
string
Current OpenAI key slot during rotation.
OPENAI_API_KEY_VERSION
string
Active version slot (v1 or v2) during rotation.
Sintesis also supports Google AI via @ai-sdk/google. No separate environment variable is listed in env.example; if you add Google AI features, add GOOGLE_GENERATIVE_AI_API_KEY following the same pattern.

WhatsApp Cloud API

All WhatsApp variables are server-only. Never expose them to the browser.
WHATSAPP_ACCESS_TOKEN
string
Permanent or temporary access token for the WhatsApp Cloud API. Generate one in the Meta for Developers portal under your app’s WhatsApp → API Setup section.
WHATSAPP_PHONE_NUMBER_ID
string
Numeric ID of the phone number you registered with the WhatsApp Cloud API. Found in Meta for Developers → WhatsApp → API Setup → Phone number ID.
WHATSAPP_VERIFY_TOKEN
string
A string you define and provide to Meta when setting up the webhook. Sintesis uses it to verify incoming webhook requests from Meta.

Upstash Redis — rate limiting

Sintesis uses Upstash Redis to enforce per-IP and per-tenant rate limits on API routes.
UPSTASH_REDIS_REST_URL
string
REST endpoint of your Upstash Redis database. Found in the Upstash console under Databases → your database → REST API.
UPSTASH_REDIS_REST_TOKEN
string
Authentication token for the Upstash Redis REST API. Found alongside the REST URL in the Upstash console.
RATE_LIMIT_IP
number
default:"120"
Maximum number of requests allowed per IP address within the configured window. Defaults to 120.
RATE_LIMIT_IP_WINDOW
string
default:"1m"
Time window for the per-IP rate limit. Uses Upstash duration syntax, e.g. 1m, 30s, 5m.
RATE_LIMIT_TENANT
number
default:"2000"
Maximum number of requests allowed per tenant within the configured window. Defaults to 2000.
RATE_LIMIT_TENANT_WINDOW
string
default:"5m"
Time window for the per-tenant rate limit. Defaults to 5m.

Domain split

Sintesis supports an optional configuration where the marketing site lives on the root domain and the application lives on an app. subdomain.
ENABLE_DOMAIN_SPLIT
boolean
default:"false"
Set to true to activate domain-split routing. When enabled, Next.js middleware redirects users between the marketing and app domains based on the route.
APP_HOST
string
default:"app.yourdomain.com"
The hostname for the application subdomain. Used in server-side redirect logic.
MARKETING_HOST
string
default:"yourdomain.com"
The hostname for the marketing/root domain. Used in server-side redirect logic.
NEXT_PUBLIC_ENABLE_DOMAIN_SPLIT
boolean
default:"false"
Client-side equivalent of ENABLE_DOMAIN_SPLIT. Must match the server-side value.
NEXT_PUBLIC_APP_HOST
string
Client-side equivalent of APP_HOST. Used in client components that build cross-domain links.
NEXT_PUBLIC_MARKETING_HOST
string
Client-side equivalent of MARKETING_HOST.

Request signing

REQUEST_SIGNING_DISABLED
number
default:"0"
Set to 1 to disable HMAC request signing for internal API calls. Useful during local development if you do not have a signing key configured. Leave at 0 in production.
REQUEST_SIGNATURE_MAX_AGE_MS
number
default:"300000"
Maximum age in milliseconds for a signed request before it is rejected as a replay. Defaults to 300000 (5 minutes).

Monitoring — Sentry

Error tracking and performance monitoring via Sentry. Sentry is only active on Vercel production builds (VERCEL_ENV=production).
SENTRY_DSN
string
The Data Source Name for your Sentry project. Found in Sentry → Settings → Projects → your project → Client Keys (DSN).
SENTRY_ENVIRONMENT
string
default:"development"
The environment name reported to Sentry (e.g. development, staging, production).
SENTRY_TRACES_SAMPLE_RATE
number
default:"0.1"
Fraction of transactions to send to Sentry for performance monitoring (0–1). Keep low in production to control costs.
SENTRY_PROFILES_SAMPLE_RATE
number
default:"0.1"
Fraction of sampled transactions that also capture a performance profile.
SENTRY_REPLAYS_ERROR_SAMPLE_RATE
number
default:"1"
Fraction of sessions where an error occurred for which a Session Replay is captured. Defaults to 1 (capture all error sessions).
SENTRY_REPLAYS_SESSION_SAMPLE_RATE
number
default:"0"
Fraction of all sessions to capture as Session Replays. Defaults to 0 to avoid storage costs in development.
SENTRY_ORG
string
Your Sentry organisation slug. Used by the Sentry webpack plugin to upload source maps during production builds. Found in Sentry → Settings → General Settings → Organization slug.
SENTRY_PROJECT
string
Your Sentry project slug. Used by the webpack plugin alongside SENTRY_ORG.

Health checks and cron

HEALTHCHECK_TOKEN
string
A secret token that uptime monitoring services must include in the Authorization header when calling the health-check endpoint. Generate a random string, e.g. openssl rand -hex 32.
CRON_SECRET
string
A secret used to authenticate requests to cron-triggered API routes (schedule runners, orphan cleanup jobs). Vercel Cron sends this automatically when you configure it in vercel.json. Generate with openssl rand -hex 32.

Complete .env.local template

# Supabase
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
SUPABASE_SERVICE_ROLE_KEY=<service-role-key>

# Google OAuth (optional)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# Email — Resend
RESEND_API_KEY=re_xxxxxxxxxxxx
[email protected]

# AI
OPENAI_API_KEY=sk-xxxxxxxxxxxx

# WhatsApp Cloud API (server-only)
WHATSAPP_ACCESS_TOKEN=
WHATSAPP_PHONE_NUMBER_ID=
WHATSAPP_VERIFY_TOKEN=

# Upstash Redis
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
RATE_LIMIT_IP=120
RATE_LIMIT_IP_WINDOW=1m
RATE_LIMIT_TENANT=2000
RATE_LIMIT_TENANT_WINDOW=5m

# Domain split (leave false for local dev)
ENABLE_DOMAIN_SPLIT=false
NEXT_PUBLIC_ENABLE_DOMAIN_SPLIT=false
APP_HOST=app.yourdomain.com
MARKETING_HOST=yourdomain.com
NEXT_PUBLIC_APP_HOST=app.yourdomain.com
NEXT_PUBLIC_MARKETING_HOST=yourdomain.com

# Request signing
REQUEST_SIGNING_DISABLED=1
REQUEST_SIGNATURE_MAX_AGE_MS=300000

# Sentry
SENTRY_DSN=
SENTRY_ENVIRONMENT=development
SENTRY_TRACES_SAMPLE_RATE=0.1
SENTRY_PROFILES_SAMPLE_RATE=0.1
SENTRY_REPLAYS_ERROR_SAMPLE_RATE=1
SENTRY_REPLAYS_SESSION_SAMPLE_RATE=0
SENTRY_ORG=
SENTRY_PROJECT=

# Health check / cron
HEALTHCHECK_TOKEN=
CRON_SECRET=

Build docs developers (and LLMs) love