Session Canvas/Docs

Environment variables

The complete reference — copy .env.example to .env and work through the tables. Columns say what breaks or degrades when a variable is missing.

Core (database, auth, URLs)

VariableRequiredNotes — and what breaks without it
DATABASE_URLYesSupabase pooled connection string (port 6543, ?pgbouncer=true). Without it every query fails.
DIRECT_URLYesSupabase direct connection string (port 5432) — used by migrations and the seed, and also validated at runtime: a deployment with only DATABASE_URL works until someone uploads a file, then 500s. Set both.
NEXT_PUBLIC_SUPABASE_URLYes*Your Supabase project URL. *Optional only with AUTH_DISABLED=true.
NEXT_PUBLIC_SUPABASE_ANON_KEYYes*Supabase anon key, same caveat.
SUPABASE_SERVICE_ROLE_KEYYes in productionPowers file storage and team invites, and the speaker-portal session cookie’s signing key is derived from it — production refuses to start portal sessions without it.
NEXT_PUBLIC_APP_URLYes in productionThe deployment’s public URL. Every absolute link — magic links, portal links, email buttons, the Airtable App URL column — is built from it; production throws rather than pointing emails at localhost. Inlined into client bundles at build time.
AUTH_DISABLEDNever in productiontrue skips Supabase auth so you can explore the admin locally. The speaker portal keeps its own token auth even then.

Email

VariableRequiredNotes
RESEND_API_KEYFor real emailWithout it, development logs every email to the console (the only place local portal sign-in links appear). In production a missing key makes sends fail loudly unless EMAIL_MOCK_MODE=1 is set explicitly.
EMAIL_FROMIn production with ResendSender in Name <address> form; also becomes the calendar-invite organizer. The fallback Resend sandbox sender only delivers to the account owner, so production requires this.
EMAIL_MOCK_MODENo1 lets a keyless production deployment mock email (one masked log line per send) instead of erroring — for demos only.

Scheduled jobs

VariableRequiredNotes
CRON_SECRETYes in productionBearer token guarding /api/cron/airtable-sync and /api/cron/task-reminders. Unset in production, the routes answer 401 and scheduled work silently stops; unset in development they stay open. Vercel sends it automatically once set. Generate: openssl rand -hex 32.
TASK_REMINDER_WINDOW_HOURSNo (default 48)How far ahead the nightly reminder looks for due tasks. Also overridable per-run with ?windowHours=.
TASK_REMINDER_DEDUPE_HOURSNo (default 20)Minimum gap before the same person is reminded again. ?dedupeHours= per run.

Airtable (all optional)

VariableRequiredNotes
ENCRYPTION_KEYFor the in-app connect flowEncrypts stored Airtable tokens at rest. Without it the connect form explains itself instead of storing anything; not needed for the env-var fallback. Losing or rotating it makes stored tokens unreadable — each event must reconnect. Generate: openssl rand -base64 32.
AIRTABLE_API_KEYFor the env fallbackPersonal access token (pat…) with data.records:read + data.records:write (add both schema.bases:* scopes to use Build the table). Works only together with AIRTABLE_BASE_ID.
AIRTABLE_BASE_IDFor the env fallbackThe app… segment of the base URL.
AIRTABLE_SUBMISSIONS_TABLENo (default “Submissions”)Table name or id for the env-var fallback.
AIRTABLE_OAUTH_CLIENT_IDFor OAuthWith the redirect URI, switches on the Connect Airtable button — both or neither.
AIRTABLE_OAUTH_REDIRECT_URIFor OAuthMust be https://your-app.example.com/api/airtable/oauth/callback, registered with Airtable byte-for-byte (no wildcards — so Vercel preview deployments can’t use OAuth; paste a token there instead).
AIRTABLE_OAUTH_CLIENT_SECRETIf issuedThe flow is PKCE-protected and works without it, but set it if Airtable issued one.