The config layer is app/config.py — a pydantic-settings object that loads the env once at startup, validates the fields, and exposes them as settings.XYZ. The FastAPI lifespan hook in app/main.py performs additional hard-fail checks on production-critical keys before the server accepts any traffic.
Generating secrets
Two cryptographic generators cover every key you need:
# 32-byte hex (JWT_SECRET, HMAC_PHONE_KEY, A2A_SIGNING_KEY, INTERNAL_API_TOKEN)
python -c "import secrets; print(secrets.token_hex(32))"
# Fernet key (ENCRYPTION_KEY)
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# VAPID keypair (VAPID_PRIVATE_KEY + VAPID_CONTACT_EMAIL)
python scripts/generate_vapid_keys.pyCore infrastructure
| Variable | Default | Purpose |
|---|---|---|
DATABASE_URLreq | postgresql+asyncpg://agnt:agntlocal@localhost:5433/agnt | AsyncPG connection string. Must use the asyncpg driver. |
REDIS_URLreq | redis://localhost:6380/0 | Redis connection for sessions, rate limits, distributed locks, send queue. |
ENVIRONMENTreq | development | One of development, staging, production. Triggers hard-fail validation in non-dev. |
APP_URLprod | http://localhost:3000 | Frontend URL for CORS, redirect URIs, email links. |
CORS_ORIGINSprod | http://localhost:3000 | Comma-separated CORS allowlist. |
Security (hard-fail in production)
| Variable | Default | Purpose |
|---|---|---|
JWT_SECRETprod | — | HS256 signing key for JWTs. Hard-fails on startup if left at the dev default. |
ENCRYPTION_KEYprod | — | Fernet encryption key for PII at rest. Generate with the Fernet generator below. |
INTERNAL_API_TOKENprod | — | Bearer token for plugin_bridge and /internal endpoints. Missing → 503 in those routes. |
META_APP_SECRETprod | — | Facebook / Instagram webhook signature verification. |
TELEGRAM_WEBHOOK_SECRETprod | — | Telegram webhook signature verification. |
STRIPE_WEBHOOK_SECRETprod | whsec_... | Stripe webhook signing secret (format: whsec_...). |
HMAC_PHONE_KEYprod | — | Independent HMAC key for phone number hashing. MUST differ from JWT_SECRET in prod. |
A2A_SIGNING_KEYprod | — | Independent HMAC key for A2A envelope signing. MUST differ from JWT_SECRET in prod. |
LLM & AI
| Variable | Default | Purpose |
|---|---|---|
ANTHROPIC_API_KEYprod | sk-ant-... | Claude API key. Required in production for all LLM-driven message processing. |
OPENAI_API_KEY | — | OpenAI API key. Optional fallback. |
OLLAMA_HOST | http://localhost:11434 | Ollama local LLM endpoint for offline dev. |
OLLAMA_MODEL | llama3:70b-q4_K_M | Ollama model name. |
RUNPOD_API_KEY | — | RunPod serverless GPU API key (alternative backend). |
RUNPOD_ENDPOINT_ID | — | RunPod endpoint ID. |
ENABLE_SEMANTIC_SEARCH | false | Enable pgvector semantic search in venue retrieval. Requires pgvector extension. |
Messaging channels
| Variable | Default | Purpose |
|---|---|---|
TELEGRAM_BOT_TOKEN | — | Telegram Bot API token from @BotFather. |
WA_360DIALOG_API_KEY | — | WhatsApp Cloud API key via 360Dialog. |
WA_PHONE_NUMBER_ID | — | WhatsApp Business Account phone number ID. |
WA_VERIFY_TOKENprod | — | WhatsApp webhook verify token. Must change from the default 'agnt_wa_verify' in prod. |
IG_PAGE_ID | — | Instagram Page ID for Messaging API. |
IG_PAGE_TOKEN | — | Instagram Page access token. |
Stripe payments
| Variable | Default | Purpose |
|---|---|---|
STRIPE_SECRET_KEYprod | sk_test_... | Stripe API secret key. Must be sk_live_* in production. |
STRIPE_PRICE_STARTER | — | Stripe price ID for the consumer Starter tier. |
STRIPE_PRICE_PRO | — | Stripe price ID for the consumer Pro tier. |
STRIPE_VENUE_PRICE_STARTER | — | Stripe price ID for the venue Starter tier. |
STRIPE_VENUE_PRICE_GROWTH | — | Stripe price ID for the venue Growth tier. |
STRIPE_VENUE_PRICE_PRO | — | Stripe price ID for the venue Pro tier. |
A2A · ClawPulse gateway
| Variable | Default | Purpose |
|---|---|---|
CLAWPULSE_API_KEY | — | Auth token for the ClawPulse gateway. Without it, A2A sends fail closed. |
CLAWPULSE_GATEWAY | https://cp.gicm.app | ClawPulse gateway base URL. |
CLAWPULSE_PLATFORM_AGENT_ID | agnt-platform | This agent's identifier on the A2A network. |
A2A_INBOX_POLL_INTERVAL_MS | 5000 | Response listener polling cadence in milliseconds. |
Push notifications
| Variable | Default | Purpose |
|---|---|---|
VAPID_PRIVATE_KEYprod | — | Web Push VAPID private key. Generate via scripts/generate_vapid_keys.py. |
VAPID_CONTACT_EMAIL | mailto:hello@agnt.ai | VAPID contact email. |
Observability
| Variable | Default | Purpose |
|---|---|---|
SENTRY_DSN | — | Sentry DSN for error reporting. Warn-only if missing. |
ALERT_WEBHOOK_URL | — | Slack or Discord webhook. Fires on DLQ > 0, circuit breaker open, error spikes, scheduler failures. |
Feature APIs (dupe search, nutrition, transport)
| Variable | Default | Purpose |
|---|---|---|
SHOPEE_AFFILIATE_ID | — | Shopee marketplace affiliate ID. |
TOKOPEDIA_AFFILIATE_ID | — | Tokopedia affiliate ID. |
LAZADA_AFFILIATE_ID | — | Lazada affiliate ID. |
SERPAPI_KEY | — | SerpAPI key for product search fallback. |
NUTRITIONIX_APP_ID | — | Nutritionix food database app ID. |
NUTRITIONIX_APP_KEY | — | Nutritionix app key. |
EDAMAM_APP_ID | — | Edamam nutrition API app ID. |
EDAMAM_APP_KEY | — | Edamam nutrition API key. |
USDA_API_KEY | — | USDA FoodData Central API key. |
LALAMOVE_API_KEY | — | Lalamove courier API key. |
LALAMOVE_SECRET | — | Lalamove API secret. |
LALAMOVE_BASE_URL | https://rest.sandbox-lalamove.com | Lalamove base URL. Flip to production to enable live delivery booking. |
LALAMOVE_MARKET | ID | Lalamove market code (ID for Indonesia). |
Email & B2B scan
| Variable | Default | Purpose |
|---|---|---|
RESEND_API_KEY | re_... | Resend transactional email API key. |
RESEND_FROM_EMAIL | AGNT <noreply@agnt.ai> | Sender address for outbound email. |
TRAINING_BOOKING_URL | — | Optional Calendly / Cal.com link appended to venue welcome email. |
FIRECRAWL_API_KEY | fc-... | Firecrawl for website scraping. Fallback: httpx + BeautifulSoup. |
TAVILY_API_KEY | tvly-... | Tavily search API for Google and TripAdvisor lookup. |
GOOGLE_PLACES_KEY | AIza... | Google Places API key. |
Storage & plugins
| Variable | Default | Purpose |
|---|---|---|
KNOWLEDGE_UPLOAD_DIR | data/knowledge_uploads | Local directory for venue knowledge base uploads. |
PAPERCLIP_WEBHOOK_URL | — | Paperclip control plane webhook for real-time events. |
PAPERCLIP_WEBHOOK_SECRET | — | Paperclip webhook signing secret. |
How the backend loads env
app/config.py uses pydantic-settings to load values in this precedence order (highest first):
- Actual environment variables set in the shell
.envfile at the repo root (only whenENVIRONMENT=development)- Defaults declared on the Settings class
Production deployments never rely on a .envfile — the orchestrator (Railway, Docker Compose, Kubernetes) injects the variables directly.