Operations
Everything Twiga reads, what it does, and where to get it. The templates in the repository are a starting point, not a complete list.
Configuration is loaded once, at import, by app/config.py. Import settings from there rather than reading the environment directly. Names are case insensitive, and unknown variables are ignored silently.
These are asserted when the module loads. Miss one and the process will not start, whatever else is configured.
META_API_VERSION
META_APP_ID
META_APP_SECRET
WHATSAPP_CLOUD_NUMBER_ID
WHATSAPP_VERIFY_TOKEN
WHATSAPP_API_TOKEN
DATABASE_URLThis is true even in mock mode, which is why .env.template.simple fills the Meta ones with the literal word placeholder. They exist to satisfy the check, and are never used.
Blank is not the same as absent
A variable that is present with no value is read as an empty string, soLLM_PROVIDER= fails validation instead of falling back to the default. Both templates ship several variables this way. To get a default, delete the line.| Variable | Default | Purpose |
|---|---|---|
META_API_VERSION | required | Graph API version used in outbound URLs. Templates use v22.0. |
META_APP_ID | required | From App settings, Basic in the Meta console. |
META_APP_SECRET | required | Verifies the signature on every incoming webhook. |
WHATSAPP_CLOUD_NUMBER_ID | required | The phone number ID messages are sent from. |
WHATSAPP_VERIFY_TOKEN | required | A string you invent, entered identically in the Meta console. |
WHATSAPP_API_TOKEN | required | Bearer token for the Cloud API. |
MOCK_WHATSAPP | False | Turns every outbound call into a no-op, disables signature checking and opens /devhooks. Development only. |
WELCOME_TEMPLATE_ID | twiga_registration_approved | Template sent when a user is approved. |
MESSAGE_CHARACTER_LIMIT | 65000 | Cap on the assembled prompt. |
| Variable | Default | Purpose |
|---|---|---|
LLM_API_KEY | none | Needed for Together, OpenAI and Google. Not needed for Ollama. |
LLM_PROVIDER | ollama | One of together, openai, ollama, modal, google. |
LLM_MODEL_NAME | a Qwen instruct model | Chat model identifier. |
AGENTIC_MODE_ENABLED | false | Swaps the single tool round trip for an agent loop. |
MAX_AGENT_ITERATIONS | 8 | Cap on that loop. |
OLLAMA_BASE_URL | http://host.docker.internal:11434/v1 | Reaches the Ollama daemon on your host from the container. |
OLLAMA_MODEL_NAME | llama3.2 | Overrides LLM_MODEL_NAME for Ollama. |
MODAL_BASE_URL, MODAL_MODEL_NAME | none | For a self-hosted Modal deployment. |
Five variables with dangerously generic names
TIMEOUT, BASE_URL, MAX_TOKENS, TEMPERATURE and REASONING_EFFORT configure the language model client, and they carry no prefix. They are read from the ambient environment, not only from your .env, so an unrelated TIMEOUT exported in a shell or set by a CI runner will quietly reconfigure Twiga.
| Variable | Default | Purpose |
|---|---|---|
EMBEDDING_API_KEY | none | Can be the same key as LLM_API_KEY if one provider serves both. |
EMBEDDING_PROVIDER | ollama | Together, OpenAI, Ollama or Modal. Google is not supported here. |
EMBEDDING_MODEL | multilingual e5 large | Must produce 1024 dimensions unless you migrate the column. |
OLLAMA_EMBEDDING_MODEL | mxbai-embed-large | Overrides the above for Ollama. |
| Variable | Purpose |
|---|---|
DATABASE_URL | The connection string. Use postgresql+asyncpg://user:pass@db:5432/twiga_db when the app runs in Docker. Alembic reuses this with the driver suffix stripped. |
DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME | Read by Docker Compose to create the local Postgres container, not by the application. Required for local development all the same. |
Active only when ENVIRONMENT is production or staging, and only when all four of these are set. If any is missing, rate limiting is skipped and a line is written to the log.
| Variable | Purpose |
|---|---|
REDIS_URL | The counter store. Unreachable Redis disables limiting rather than blocking traffic. |
USER_MESSAGE_LIMIT | Messages per user per window. |
GLOBAL_MESSAGE_LIMIT | Messages across all users per window. |
TIME_TO_LIVE | Window length in seconds. A day is 86400. |
Only REDIS_URL appears in the templates. The other three have to be added by hand, which is easy to miss when standing up production.
Needed only for the onboarding and settings screens, which require a verified business account. Each is optional at startup and fails on the first request that needs it.
WHATSAPP_BUSINESS_PRIVATE_KEY
WHATSAPP_BUSINESS_PRIVATE_KEY_PASSWORD
FLOW_TOKEN_ENCRYPTION_KEY
ONBOARDING_FLOW_ID
SUBJECTS_CLASSES_FLOW_ID| Variable | Default | Purpose |
|---|---|---|
ENVIRONMENT | local | One of production, staging, development, local. Controls Redis, rate limiting, welcome templates and whether unknown numbers get a ready-made test account. |
DEBUG | True | Verbose logging. |
USER_INACTIVITY_THRESHOLD_HOURS | 24 | How long before a user is marked inactive. |
TWIGA_ENV | .env | Path to the environment file. Must be set in the real environment, since it is read before any file is opened. |
Optional. Tracing turns on only when the key is present and the flag is true.
LANGSMITH_API_KEY=your_key
LANGSMITH_PROJECT=twiga-whatsapp-chatbot
LANGSMITH_TRACING=True
LANGSMITH_ENDPOINT=https://api.smith.langchain.comBUSINESS_ENV appears in both templates and is read by nothing. Safe to delete.LLM_MODEL_NAME defaults to a Llama model is wrong on both counts. The default is a Qwen model, and leaving the line blank gives you an empty string rather than the default.XXX: markers when using OpenAI is out of date. Those markers have moved.EMBEDDING_MODEL, MESSAGE_CHARACTER_LIMIT and USER_INACTIVITY_THRESHOLD_HOURS.