Pitchbar reads its configuration from the standard Laravel
.env, with overrides for some keys available via the
platform admin's App Settings page so you can swap
Stripe / mail / LLM credentials without redeploying.
Required
Variable
Why
APP_KEY
Encryption master key. Generate with php artisan key:generate. Never rotate without a re-encrypt migration.
APP_URL
Public URL, used to build widget snippets, OAuth callbacks, signed URLs.
DB_*
Postgres connection.
REDIS_HOST
Cache, sessions, queue, hot-path retrieval cache, conversation history cache.
QUEUE_CONNECTION
Set to redis in production.
SESSION_DRIVER
redis in production.
CACHE_DRIVER
redis.
WIDGET_JWT_SECRET
HS256 signing secret for visitor JWTs. โฅ 32 random bytes.
LLM provider
At least one of the following:
Variable
Provider
CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN
Cloudflare Workers AI (preferred). Auto-binds Llama 3.3 70B + bge-base-en-v1.5.
OPENAI_API_KEY
OpenAI direct.
OPENROUTER_API_KEY
OpenRouter (router across many providers).
Set LLM_PROVIDER to force a binding (cloudflare,
openai, openrouter). When unset, the resolver
picks based on which keys are available, in the priority above.
Vector store
Variable
Provider
CLOUDFLARE_VECTORIZE_INDEX
Cloudflare Vectorize index name. Uses CLOUDFLARE_ACCOUNT_ID + token. Default pitchbar-chunks.
QDRANT_URL + QDRANT_API_KEY
Qdrant.
Set VECTOR_PROVIDER to force. Auto-binds based on
available keys (Vectorize preferred).
Crawler
Variable
Strategy
(uses CLOUDFLARE_*)
Cloudflare Browser Rendering. Preferred.
BROWSERLESS_TOKEN + BROWSERLESS_URL
Browserless fallback.
(none)
Plain HTTP. Free; no JS rendering.
Variable
Purpose
CRAWL_MAX_PAGES_PER_SOURCE
Max pages fan-out per Source. Default 500 (raised from 25 โ buyers adding 100-URL sitemaps were silently losing 75 pages). Cap applies after sitemap-index recursion + dedupe.
CRAWL_PAGE_DISPATCH_DELAY
Per-page delay (seconds) when fanning out sitemap pages so Cloudflare Browser Rendering's small per-account concurrency doesn't 429. Default 2.
CLOUDFLARE_BROWSER_RENDERING
Set to false to force the Browserless / plain-HTTP path even when Cloudflare keys are present. Default true.
RAG / retrieval
Variable
Purpose
RAG_CONFIDENCE_THRESHOLD
Default similarity threshold for newly created agents. 0.5 for Cloudflare bge-base-en-v1.5 (its ANN cosine peaks lower than OpenAI's); 0.78 for OpenAI text-embedding-3-small. Auto-resolves based on LLM_PROVIDER.
RAG_RERANK_ENABLED
Set to false to disable the bge-reranker-base cross-encoder pass. Default true when Cloudflare keys are present.
VECTOR_DIM
Embedding dimension override. Leave unset to auto-resolve from the configured embed model via the known-model map (bge-base=768, bge-m3=1024, text-embedding-3-small=1536, etc.). Set explicitly only when pointing at an unlisted model. Changing this on an existing install needs php artisan vector:rebuild-index โ the Vectorize index is provisioned at the dim active when it was first created and cannot be resized in place.
Cloudflare AI Gateway (optional)
Variable
Purpose
CLOUDFLARE_AI_GATEWAY_URL
If set, Workers AI calls route through Cloudflare's AI Gateway โ observability, caching, rate limits.
CLOUDFLARE_CHAT_MODEL
Default @cf/meta/llama-3.3-70b-instruct-fp8-fast.
CLOUDFLARE_EMBED_MODEL
Default @cf/baai/bge-base-en-v1.5.
CLOUDFLARE_VECTORIZE_INDEX
Vectorize index name. Default pitchbar-chunks.
OPENROUTER_CHAT_MODEL
OpenRouter chat model slug. Default meta-llama/llama-3.3-70b-instruct:free.
Qdrant collection name when using the Qdrant vector store fallback. Default pitchbar_chunks.
S3 / R2 (object storage for file uploads)
Variable
Purpose
AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
Bucket credentials. For Cloudflare R2, use the R2 token's key/secret pair.
AWS_DEFAULT_REGION
Region of the bucket. Use auto for R2.
AWS_BUCKET
Bucket name.
AWS_ENDPOINT
Custom S3-compatible endpoint. Required for R2 (e.g. https://<accountid>.r2.cloudflarestorage.com). Leave blank for native AWS S3.
AWS_USE_PATH_STYLE_ENDPOINT
true for R2 and most non-AWS S3 implementations; false for native AWS.
Frontend (Vite)
Variable
Purpose
VITE_WIDGET_CDN_URL
Base URL the visitor widget loader uses. Defaults to http://localhost:5173 in dev. In production, point at the public origin serving /widget/widget.js.
Default "Powered by Pitchbar" label. Override in app_settings for white-label.
BRANDING_URL
Where the label links to.
BRANDING_FOOTER_LOGO_PATH
Storage path to the footer logo image.
Observability
Variable
Purpose
SENTRY_LARAVEL_DSN
Error reporting. (Some hosts still ship the legacy SENTRY_DSN name; the Laravel SDK reads SENTRY_LARAVEL_DSN as of v4.x โ keep both in .env for back-compat if you migrated.)
Google OAuth (Drive + Docs) โ register an OAuth client in the GCP console with drive.readonly and documents.readonly scopes.
Marketing demo widget
Variable
Purpose
MARKETING_DEMO_AGENT_ID
UUID of a published agent that powers the live demo widget on the marketing pages. Add the marketing site URL to that agent's allowed_origins.
DEMO
Set to true to open Login / Get started links in a new tab on the marketing site โ useful for reviewers exploring a CodeCanyon live demo without losing the marketing page when they hop into the app.
Queue tick (Cloudflare Worker cron)
Variable
Purpose
INTERNAL_QUEUE_TOKEN
Shared bearer secret protecting POST /api/v1/internal/queue-tick. The Cloudflare Worker cron pings this every 60s to drive the queue when in-cluster scheduling isn't available.
App Settings overrides
The app_settings singleton row stores plaintext-encrypted
overrides for:
Stripe secret + webhook secret + publishable key.
Mail driver settings.
Cloudflare / OpenAI / OpenRouter keys.
Branding (label, URL, logo).
The AppSettingsOverrideServiceProvider reads this on boot
and merges into config(). Setting things via the admin
panel is preferred for production โ you don't have to re-deploy when a
key rotates.
APP_KEY rotation requires a manual migration. The
encrypted columns in app_settings were sealed with the
old key; rotating without re-encrypting renders them unreadable.