Environment Variables
Every environment variable explained with defaults and examples.
Quick Reference
| Variable | Required | Default | Purpose |
|---|---|---|---|
| — | Django secret key for token signing | |
| | Enable debug mode | |
| (prod) | | Comma-separated allowed hostnames |
| — | PostgreSQL connection string | |
| — | Redis URL for Celery task queue | |
| — | Redis URL for WebSocket channel layer | |
| — | AES-256 encryption key for API credentials | |
| — | AI chatbot API key | |
| — | Meta (Facebook) App ID | |
| — | Meta App Secret | |
| — | Public URL for webhook callbacks | |
| | Enable WhatsApp auto-reply chatbot | |
| | Allowed frontend origins |
Detailed Explanations
Core Django
SECRET_KEY
SECRET_KEYenv
SECRET_KEY=django-insecure-change-me-in-production-to-64-random-chars
What it does: Used to sign JWT tokens, CSRF tokens, and session cookies. If this changes, all existing tokens become invalid (users get logged out).
How to generate:
bash
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
Security: In production, use a 64+ character random string. Never commit to git.
DEBUG
DEBUGenv
DEBUG=True # Development
DEBUG=False # Production
What it does:
- → Detailed error pages, SQL query logging, no HTTPS requirement
True - → Generic error pages, HTTPS enforced, static files must be pre-collected
False
Warning: Never run in production. It exposes stack traces, database queries, and environment variables in error pages.
DEBUG=TrueALLOWED_HOSTS
ALLOWED_HOSTSenv
ALLOWED_HOSTS=localhost,127.0.0.1,api.metapilot.io
What it does: Django rejects requests with headers not in this list. Prevents HTTP Host header attacks.
HostDevelopment: Leave as (accept all) or .
Production: Set to your exact domain(s).
*localhostDatabase
DATABASE_URL
DATABASE_URLenv
# Local development
DATABASE_URL=postgresql://metapilot:metapilot@localhost:5432/metapilot
# Docker
DATABASE_URL=postgresql://metapilot:metapilot@db:5432/metapilot
# Production (managed PostgreSQL)
DATABASE_URL=postgresql://user:password@db-host.aws.com:5432/metapilot?sslmode=require
Format:
postgresql://USER:PASSWORD@HOST:PORT/DATABASE_NAMEWhat it does: Connection string for PostgreSQL. Parsed by in settings.
dj-database-urlWhy PostgreSQL? JSONB columns for flexible data (tags, metadata, template params), for distributed job scheduling, excellent indexing.
SELECT FOR UPDATE SKIP LOCKEDRedis
CELERY_BROKER_URL
CELERY_BROKER_URLenv
# Local
CELERY_BROKER_URL=redis://localhost:6379/0
# Docker
CELERY_BROKER_URL=redis://redis:6379/0
# Production (managed Redis)
CELERY_BROKER_URL=rediss://default:password@redis-host:6380/0
What it does: Redis database 0 is used as the Celery message broker. Task payloads (e.g., "send these 1000 messages") are queued here.
Database number : Redis supports 16 databases (0-15). We use 0 for Celery, 1 for Channels.
0CHANNEL_REDIS_URL
CHANNEL_REDIS_URLenv
CHANNEL_REDIS_URL=redis://localhost:6379/1
What it does: Redis database 1 is used for Django Channels (WebSocket pub/sub). Inbox real-time events are broadcast through this.
Why a separate database? Isolation. Celery's high-throughput task queue doesn't interfere with WebSocket message delivery.
Encryption
FERNET_KEY
FERNET_KEYenv
FERNET_KEY=your-44-character-base64-encoded-key-here=
What it does: Symmetric encryption key for encrypting tenant API credentials (Meta access tokens, phone number IDs, app secrets) stored in the table.
tenant_configsHow to generate:
bash
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# Output: something like: s2a3d4f5g6h7j8k9l0a1s2d3f4g5h6j7k8l9a0s1d2f3g4h5=
Security rules:
- Generate once, never change (or all encrypted data becomes unreadable)
- Store only in environment variables, never in code
- Back up securely (if lost, all encrypted credentials are permanently lost)
- 44 characters, base64-encoded, ending with
=
What happens if you lose it: All tenant API credentials become undecryptable. You'd need to re-enter every tenant's Meta access token, phone number ID, etc.
AI / Chatbot
OPENROUTER_API_KEY
OPENROUTER_API_KEYenv
OPENROUTER_API_KEY=sk-or-v1-abcdef1234567890
What it does: API key for OpenRouter — a unified gateway to multiple AI models (GPT-4o, Llama 4, Gemini).
Used by:
- Platform chatbot (help assistant)
- WhatsApp auto-reply chatbot (customer-facing)
- Image analysis (vision AI)
Not required if: You disable AI features ( and don't use the platform chatbot).
WA_CHATBOT_ENABLED=FalseHow to get one: Sign up at https://openrouter.ai/ → API Keys → Create Key
WA_CHATBOT_ENABLED
WA_CHATBOT_ENABLEDenv
WA_CHATBOT_ENABLED=True # Auto-reply to customer messages
WA_CHATBOT_ENABLED=False # Just receive and store messages
What it does: When enabled, incoming WhatsApp messages trigger an AI-generated auto-reply. When disabled, messages are stored and shown in the inbox, but no automatic response is sent.
Meta (WhatsApp) Integration
META_APP_ID
META_APP_IDenv
META_APP_ID=123456789012345
What it does: Your Meta (Facebook) App ID from the Meta Developer Console. Used for API authentication and webhook configuration.
META_APP_SECRET
META_APP_SECRETenv
META_APP_SECRET=abc123def456ghi789
What it does: Used to verify webhook signatures (HMAC-SHA256). Meta signs every webhook payload with this secret, and we verify the signature to prevent fake webhooks.
Note: Per-tenant app secrets are stored encrypted in . This global setting is a fallback.
TenantConfigWEBHOOK_BASE_URL
WEBHOOK_BASE_URLenv
WEBHOOK_BASE_URL=https://api.metapilot.io
What it does: The public URL where Meta sends webhook callbacks. Used to generate per-tenant webhook URLs:
{WEBHOOK_BASE_URL}/api/wa-chatbot/webhook/{tenant_id}/
Development: Use ngrok or similar to expose localhost:
bash
ngrok http 8000
# Then set: WEBHOOK_BASE_URL=https://abc123.ngrok.io
CORS
CORS_ALLOWED_ORIGINS
CORS_ALLOWED_ORIGINSenv
# Development
CORS_ALLOWED_ORIGINS=http://localhost:3000
# Production
CORS_ALLOWED_ORIGINS=https://app.metapilot.io,https://admin.metapilot.io
What it does: Browsers block cross-origin requests by default. This tells the API which frontend origins are allowed to make requests.
Common mistake: Forgetting to add your frontend URL here → browser console shows errors.
CORS policyFeature Flags
TEMPLATE_AUTO_SYNC
TEMPLATE_AUTO_SYNCenv
TEMPLATE_AUTO_SYNC=True
What it does: When enabled, Celery Beat automatically syncs WhatsApp templates from Meta's API every 5 minutes. Disable to reduce API calls during development.
AUDIT_LOG_ENABLED
AUDIT_LOG_ENABLEDenv
AUDIT_LOG_ENABLED=True
What it does: When enabled, security-relevant actions (login, logout, create, delete) are logged to the table.
audit_logsFull .env.example
.env.exampleenv
# ═══════════════════════════════════════════════════
# METAPILOT — Environment Configuration
# ═══════════════════════════════════════════════════
# ── Core Django ────────────────────────────────────
SECRET_KEY=django-insecure-change-this-in-production
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
# ── Database ───────────────────────────────────────
DATABASE_URL=postgresql://metapilot:metapilot@localhost:5432/metapilot
# ── Redis ──────────────────────────────────────────
CELERY_BROKER_URL=redis://localhost:6379/0
CHANNEL_REDIS_URL=redis://localhost:6379/1
# ── Encryption ─────────────────────────────────────
# Generate: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
FERNET_KEY=
# ── AI / Chatbot ───────────────────────────────────
OPENROUTER_API_KEY=
WA_CHATBOT_ENABLED=False
# ── Meta (WhatsApp) ───────────────────────────────
META_APP_ID=
META_APP_SECRET=
WEBHOOK_BASE_URL=http://localhost:8000
# ── CORS ───────────────────────────────────────────
CORS_ALLOWED_ORIGINS=http://localhost:3000
# ── Feature Flags ──────────────────────────────────
TEMPLATE_AUTO_SYNC=True
AUDIT_LOG_ENABLED=True
# ── Email (Optional) ──────────────────────────────
EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
EMAIL_HOST=
EMAIL_PORT=587
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
EMAIL_USE_TLS=True
Environment-Specific Configurations
| Setting | Development | Production |
|---|---|---|
| | |
| Any string | 64+ random chars |
| | Exact domains |
| Local PostgreSQL | Managed PostgreSQL (SSL) |
| Local Redis | Managed Redis (TLS) |
| | |
| | |
| ngrok URL | Production domain |