answerLoopsanswerLoops Docs
Reference

Environment Variables

Complete reference for every environment variable recognised by answerLoops.

All variables are set in your .env file (local) or in your hosting provider's environment configuration (production). Variables marked Required must be present or the app will refuse to start.

answerLoops uses Auth.js v5. The correct variable names are AUTH_SECRET and AUTH_URLnot NEXTAUTH_SECRET / NEXTAUTH_URL. If you are migrating from an older deployment, rename them.

Core (required)

VariableDescription
DATABASE_URLPostgreSQL connection string, e.g. postgresql://user:pass@host:5432/dbname
DIRECT_DATABASE_URLNon-pooled connection string for LISTEN/NOTIFY only (config hot-reload, live team and dashboard updates). Required whenever DATABASE_URL is a pooled connection (Neon -pooler, PgBouncer, etc.) — falls back to DATABASE_URL otherwise
AUTH_SECRETRandom 32-byte secret used to sign Auth.js v5 session tokens and the OAuth state for channel connect flows. Generate with openssl rand -hex 32
AUTH_URLFull public URL of your deployment, e.g. https://answerloops.com. Used by Auth.js for OAuth callbacks
ENCRYPTION_KEY32-byte hex key used to encrypt stored API keys at rest. Generate with openssl rand -hex 32. In production (NODE_ENV=production) it is mandatory — saving a credential without it throws instead of storing plaintext

Networking

VariableDescription
TRUST_PROXY_HOPSNumber of proxies between the public internet and the app. Defaults to 1. Rate limiters resolve the client IP by counting this many entries in from the right of x-forwarded-for, so a caller-supplied prefix on that header can't be used to mint a fresh bucket per request. Set to 2 for Cloudflare in front of a platform load balancer. cf-connecting-ip takes precedence when present
ORIGIN_VERIFY_SECRETOptional. When set, every public pre-auth POST route (/api/mcp, /api/v1/agent/*, /api/widget/chat) rejects any request missing a matching x-origin-verify header — without it, the proxy-supplied client-IP header can be spoofed by hitting the origin directly instead of through your edge proxy. Requires a matching rule on your CDN/edge proxy; see the self-hosting guide

Multi-domain

VariableDescription
NEXT_PUBLIC_APP_URLOptional. Base URL of a dedicated dashboard subdomain (e.g. https://app.example.com) that shares the same deployment as your root domain. When set, marketing-page CTAs link there instead of a relative /dashboard path. Unset by default — most deployments serve everything from one domain
AUTH_COOKIE_DOMAINOptional. Shares the Auth.js session cookie across every subdomain of your apex (e.g. .example.com). Required alongside NEXT_PUBLIC_APP_URL, since a session cookie is host-only by default — without it, a user signed in on the root domain appears logged out on the app subdomain

OAuth provider

Google is the only sign-in provider answerLoops configures. Both variables are required.

VariableDescription
AUTH_GOOGLE_IDGoogle OAuth client ID
AUTH_GOOGLE_SECRETGoogle OAuth client secret

Callback URL to register: https://<your-domain>/api/auth/callback/google

DISCORD_CLIENT_ID below is a separate thing — the "connect a Discord server" flow inside the product, not a sign-in method. There is no Discord or GitHub OAuth provider for dashboard sign-in.

AI providers

These are platform-wide defaults. Individual orgs can override the AI model in Settings → AI Model.

VariableDescription
OPENAI_API_KEYOpenAI API key — enables GPT models and OpenAI embeddings
ANTHROPIC_API_KEYAnthropic API key — enables Claude models
GOOGLE_GENERATIVE_AI_API_KEYGoogle AI API key — enables Gemini models
GROQ_API_KEYGroq API key — enables fast open-weight model inference
MISTRAL_API_KEYMistral API key — enables Mistral models

At least one AI provider key is strongly recommended. Without one, AI draft replies are disabled and KB search falls back to keyword matching.

Widget chat runtime

VariableDescription
COPILOTKIT_TELEMETRY_DISABLEDOpts the widget chat's CopilotKit runtime out of its telemetry ping. Defaults to true; set to false to opt back in.

Discord

VariableDescription
DISCORD_TOKENBot token from the Discord Developer Portal. Required on both the app service (for the channel picker UI) and the bot service
DISCORD_CLIENT_IDDiscord application client ID. Optional — when set, onboarding offers a 1-click "Add to Discord" flow instead of asking for a manually-created bot token and channel IDs
DISCORD_APPLICATION_IDDiscord application ID (same value as DISCORD_CLIENT_ID in most setups). Required to register the /ask and /summarize slash commands
DISCORD_GUILD_IDID of the Discord server to monitor
BOT_SECRETShared secret between the bot and the app for authenticating ingest requests
BOT_TARGET_URLBase URL the bot posts ingested messages to, e.g. https://answerloops.com — no trailing slash

Slack

VariableDescription
SLACK_CLIENT_IDSlack app client ID
SLACK_CLIENT_SECRETSlack app client secret
SLACK_SIGNING_SECRETUsed to verify that incoming Slack events are genuine
SLACK_POLL_INTERVAL_SECONDSHow often (in seconds) the app polls Slack for new messages. Defaults to 60

Google Chat

VariableDescription
GOOGLE_CHAT_SERVICE_ACCOUNT_JSONFull service-account JSON key, as a single-line string. Authenticates outgoing replies to the Chat API.
GOOGLE_CHAT_ENDPOINT_URLPublic HTTP endpoint URL for the Chat app's connection settings. Must exactly match what's configured in Google Cloud — used to verify incoming request tokens.

GitHub

VariableDescription
GITHUB_APP_IDNumeric ID of your GitHub App
GITHUB_APP_PRIVATE_KEYPEM-encoded private key generated in the GitHub App settings
GITHUB_WEBHOOK_SECRETSecret used to verify GitHub webhook payloads. Generate with openssl rand -hex 32 — paste without trailing newline. Required — /api/github/webhook returns 503 until it is set
GITHUB_APP_SLUGURL slug for your GitHub App, e.g. answerloops

Telegram

VariableDescription
TELEGRAM_BOT_TOKENBot token from @BotFather on Telegram

Resend (email)

VariableDescription
RESEND_API_KEYResend API key — used for both outbound transactional email and the email ingest channel
RESEND_FROMDefault sender address for transactional emails, e.g. [email protected]
RESEND_WAITLIST_FROMSender address for waitlist emails (can be the same as RESEND_FROM)
RESEND_WEBHOOK_SECRETSvix signing secret (whsec_...) for Resend's outbound delivery-status webhooks (bounces/complaints) — bounce/complaint tracking is unavailable without it, sending and inbound ingest still work
GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRETGoogle Cloud OAuth client for the "Connect Gmail" send-only integration — optional, without it that feature is unavailable
GMAIL_REDIRECT_URIOptional override for the Gmail OAuth callback URL (defaults to <app-url>/api/email/gmail/callback)
OUTLOOK_CLIENT_ID / OUTLOOK_CLIENT_SECRETMicrosoft Entra app registration for the "Connect Outlook" send-only integration — optional, without it that feature is unavailable
OUTLOOK_REDIRECT_URIOptional override for the Outlook OAuth callback URL (defaults to <app-url>/api/email/outlook/callback)

Firecrawl

VariableDescription
FIRECRAWL_API_KEYFirecrawl API key — required for KB URL crawl imports
FIRECRAWL_API_URLOptional. Point at a self-hosted Firecrawl instance instead of the managed service

Per-org integrations (no environment variable)

Discourse, Circle, and the Notion KB source are configured entirely per-organization — there is no platform-wide app or shared credential to set here. Each org pastes its own token or secret in Settings, encrypted at rest with ENCRYPTION_KEY (above).

IntegrationWhere it's configuredSetup guide
DiscourseIntegrations → DiscourseDiscourse
CircleIntegrations → CircleCircle
Notion KB sourceIntegrations → NotionNotion

Analytics tuning

VariableDescription
ROI_MINUTES_PER_TICKETOptional. Minutes assumed saved per deflected ticket, used in the ROI-hours-saved calculation. Defaults to a reasonable estimate — see lib/analytics/roi.ts
ROI_STAFF_HOURLY_RATEOptional. Hourly staff rate used to convert hours saved into a dollar figure. Same defaulting behaviour

Web Push (VAPID)

VariableDescription
VAPID_PUBLIC_KEYVAPID public key for browser push notifications
VAPID_PRIVATE_KEYVAPID private key
VAPID_EMAILContact email included in VAPID headers, e.g. mailto:[email protected]

Generate a VAPID key pair with:

npx web-push generate-vapid-keys

Sentry (error tracking)

VariableDescription
SENTRY_DSNDSN from your Sentry project's Client Keys settings. Unset disables error tracking entirely — no requests to Sentry are made
SENTRY_AUTH_TOKENOptional. Only needed to upload source maps at build time for readable production stack traces
SENTRY_ORGOptional. Your Sentry organization slug, used alongside SENTRY_AUTH_TOKEN
SENTRY_PROJECTOptional. Your Sentry project slug, used alongside SENTRY_AUTH_TOKEN

Billing / deployment mode

VariableDescription
DEPLOYMENT_MODESet to cloud only on answerLoops' own managed SaaS deployment. Determines whether plan-tier feature gating (Discord/Slack integrations, CSAT scoring, simulation, knowledge gap dashboard, custom AI model config) applies at all. Any other value, or unset, means self-hosted: unmetered, every feature unlocked, no Stripe required
STRIPE_SECRET_KEYStripe secret API key. Required when DEPLOYMENT_MODE=cloud — its absence there is treated as a misconfiguration (billing shows an error state) rather than falling back to self-hosted/unlimited
STRIPE_WEBHOOK_SECRETSigning secret for the Stripe webhook endpoint (/api/billing/webhook), used to verify subscription lifecycle events
STRIPE_PUBLISHABLE_KEYPublishable key (pk_...) for the embedded checkout form on /checkout. Read at request time — deliberately not NEXT_PUBLIC_-prefixed, since that prefix is substituted at build time and could never be set by someone running a prebuilt image. Safe to expose in client code by design — it can only create payment attempts, never read or move money. Without it that page shows a disabled state instead of a card form
STRIPE_PRICE_STANDARD / STRIPE_PRICE_PRO / STRIPE_PRICE_ENTERPRISEStripe Price IDs used for each paid plan's checkout session. Each var must hold the price for the plan it names — the amount shown in the app comes from the code, while the amount charged comes from Stripe, so a mismatch bills the wrong figure without erroring
STRIPE_PRICE_STANDARD_ANNUAL / STRIPE_PRICE_PRO_ANNUAL / STRIPE_PRICE_ENTERPRISE_ANNUALYearly Stripe Price IDs for the same three plans. Selected when a visitor picks annual billing on the pricing page. A plan without one declines annual checkout rather than billing the monthly price.

DEPLOYMENT_MODE was introduced to stop deployment type from being inferred from whether STRIPE_SECRET_KEY happened to be set — a missing or rotated key on the cloud deployment used to silently degrade every paying org to unmetered/unlimited instead of surfacing as a misconfiguration.

On this page