Configuration
every key, where it lives.
VOX reads its keys from the console's key file or the process environment. This page lists every variable, what it is for and where to get it. A template with every name and no values: deploy/vox.env.example.
01Where keys live
| Where | How | Notes |
|---|---|---|
| Console → Setup (single-user) | Each key has a live test for Anthropic, ElevenLabs, Twilio, Telnyx and Google, plus the optional Telegram bot (getMe) and SMTP server (TLS and log-in, no mail sent). | Saved to app/data/secrets.env, mode 0600, ignored by git. The console never shows a key back, only its last four characters. |
| Process environment | deploy/vox.env for Docker, EnvironmentFile for systemd, or your shell. | The environment wins over the file; the console then shows that key read-only. |
The code does not read a .env file by itself, neither Python nor Node. To use one by hand: chmod 600 .env, then set -a; . ./.env; set +a in the same terminal. A key that ever reached git is leaked: replace it.
In SaaS mode every key is the operator's. Customers never enter or see a key; setup routes answer 403 OPERATOR_ONLY in a customer's console.
The worker re-reads the key file on every round. The media server and the console token are read at start, so restart after changing them.
02Required for calls
python -m voice check lists all missing ones at once and exits with code 2.
| Variable | Used for | Where to get it |
|---|---|---|
ANTHROPIC_API_KEY Required | the conversation (Claude), research, agent tasks, mail triage, the briefing | Anthropic Console → Settings → API Keys → Create Key. Check that Billing has credit. console.anthropic.com |
ELEVENLABS_API_KEY Required | speech-to-text and text-to-speech | ElevenLabs → Settings → API Keys. elevenlabs.io |
ELEVENLABS_VOICE_ID Required | the voice VOX speaks with | ElevenLabs → Voices → pick a voice that handles your call language → Copy voice ID. Listen to it on a real phone line first. |
TWILIO_ACCOUNT_SID Required | telephony | Twilio Console → Account Info (starts with AC). console.twilio.com |
TWILIO_AUTH_TOKEN Required | telephony, webhook signatures, hanging up when the disclosure fails | Twilio Console → Account Info → Show |
TWILIO_FROM_NUMBER Required | the number VOX calls from | Twilio Console → Phone Numbers → Active numbers. Voice-capable, international format (+…). |
VOX_PUBLIC_HOST Required | the public host the provider reaches | Your domain, host name only, e.g. vox.example.com. No https://, no path (a scheme and a trailing / are stripped). |
03Telnyx instead of Twilio
Calls go through Twilio whenever its three variables are all set; otherwise through Telnyx Call Control v2 as soon as any TELNYX_ variable is set, and then all four are required.
| Variable | Used for | Where to get it |
|---|---|---|
TELNYX_API_KEY | placing the call and hanging it up when the disclosure fails | Telnyx portal → Keys & Credentials → API Keys |
TELNYX_FROM_NUMBER | the number VOX calls from | Telnyx portal → My Numbers |
TELNYX_CONNECTION_ID | the Call Control Application the calls go through | Telnyx portal → Voice → Programmable Voice → your app → Application ID |
TELNYX_PUBLIC_KEY | verifying that a webhook really came from Telnyx (Ed25519) | Telnyx portal → Keys & Credentials → Public Key (Base64) |
- Call Control webhookIn the Telnyx portal set the Call Control Application's webhook URL to
https://<VOX_PUBLIC_HOST>/call/telnyx(API v2) and assignTELNYX_FROM_NUMBERto it. Audio streams towss://<VOX_PUBLIC_HOST>/ws/telnyx. - TextsMessaging → create a Messaging Profile, API v2, inbound webhook
https://<VOX_PUBLIC_HOST>/sms/telnyx, and assign the number. Without it no text goes out or comes in. - Calls inSet
VOX_CALL_PIN. On Telnyx the PIN is typed only (keypad), not spoken.
WhatsApp stays Twilio-only. The rings, the disclosure and the one-time stream secret work the same. Telnyx has Unverified against a real account.
04Optional
| Variable | Used for | Where to get it |
|---|---|---|
VOX_ADMIN_TOKEN | a fixed console token (16+ characters) instead of a new random one at every start. Recommended for Docker and systemd. | Any random string, e.g. python -c "import secrets; print(secrets.token_urlsafe(32))" |
GOOGLE_OAUTH_CLIENT_FILE | single-user: path to your Google OAuth client JSON of type Desktop app (Gmail read-only, Calendar) | Google Cloud Console: enable the Gmail API (and Calendar API), add yourself as a test user, Credentials → OAuth client ID → Desktop app → download JSON. Then python -m voice mail-auth / calendar-auth. |
TWILIO_WHATSAPP_FROM | texts via WhatsApp instead of SMS | Twilio Console → WhatsApp senders. Messages outside the 24-hour window need an approved template. |
VOX_CALL_PIN | 4–8 digits the owner enters when calling VOX. Without it inbound calls are refused politely (caller ID can be spoofed). | Choose it. |
TELEGRAM_BOT_TOKEN | messages to your verified Telegram chat | @BotFather → /newbot |
TELEGRAM_WEBHOOK_SECRET | commands by Telegram (webhook https://<VOX_PUBLIC_HOST>/telegram); updates without it get 403 | 16–256 of A-Z a-z 0-9 _ - you make up |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM | e-mail to your verified address; in SaaS mode also account e-mails | Your mail provider. Port 587 STARTTLS (default) or 465 TLS; VOX never sends unencrypted. SMTP_FROM like VOX <noreply@example.com>; the domain should have SPF and DKIM. |
VOX_TTS_BACKEND, VOX_PIPER_URL | piper = the voice comes from your own Piper server | Local speech |
VOX_STT_BACKEND, VOX_WHISPER_URL | whisper = recognition by your own whisper.cpp server | Local speech |
File path settings must point to a regular file (not a symlink) of at most 64 KB inside the home directory or the VOX data directory.
05SaaS keys
| Variable | Used for | Where to get it |
|---|---|---|
STRIPE_SECRET_KEY | Checkout, Customer Portal, cancelling a deleted customer's subscription | Stripe Dashboard → Developers → API keys (sk_test_… for test mode), or a restricted key. dashboard.stripe.com |
STRIPE_WEBHOOK_SECRET | verifying webhook events | The signing secret (whsec_…) of the endpoint you create (Earnings) |
GOOGLE_OAUTH_WEB_CLIENT_FILE | customers connect their own Gmail and Calendar | Operator panel → Google. Or place the file at data/saas/google_oauth_web_client.json. |
TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET, SMTP_* | customers' Telegram and e-mail messages through your bot and server | As above. Never copied into a customer's environment. |
06Other variables
| Variable | Where | Meaning |
|---|---|---|
VOX_SAAS_URL | process environment only | SaaS: base URL for links in e-mails. If unset: the branding base URL, else https://VOX_PUBLIC_HOST. |
VOX_DATA_DIR | environment | another data directory than app/data (same as --data-dir) |
VOX_ADMIN_PORT / VOX_MEDIA_PORT | Docker Compose | ports for the compose file (8766 / 8765), not VOX settings |
VOX_PUBLIC_DEMO | environment | 1 serves a directory made by scripts/make_demo.py read-only. Never on real data. |
NLTK_DATA | environment | where NLTK finds punkt_tab if you installed it with setup-nltk --dir |
VOX_PIPER_BIN, VOX_PIPER_MODEL | shell only | the terminal briefing speaks through a local Piper binary instead of a server |
VOX_WAKEWORD_MODEL, VOX_WAKEWORD_MELSPEC, VOX_WAKEWORD_EMBEDDING, VOX_WAKEWORD_THRESHOLD | environment | only for wakeword-test with models you bring (below) |
07Local speech (optional)
Off by default. With it, calls use your own Piper (text-to-speech) and/or whisper.cpp (speech-to-text) servers instead of ElevenLabs. VOX installs, bundles and downloads none of them; it only talks to them over HTTP. There is no fallback: a chosen engine that does not answer is an error, never a quiet switch to ElevenLabs. Plain http:// is accepted only on this computer (127.0.0.1, localhost, ::1).
| You choose | ElevenLabs keys a call still needs |
|---|---|
| nothing (default) | ELEVENLABS_API_KEY + ELEVENLABS_VOICE_ID |
VOX_TTS_BACKEND=piper | ELEVENLABS_API_KEY (speech-to-text) |
VOX_STT_BACKEND=whisper | both (the voice is still ElevenLabs) |
| both | none |
VOX_TTS_BACKEND=piper
VOX_PIPER_URL=http://127.0.0.1:5000
VOX_STT_BACKEND=whisper
VOX_WHISPER_URL=http://127.0.0.1:8080/inference
python -m voice local-check --lang en
Use rhasspy/piper (MIT), never OHF-Voice/piper1-gpl (GPL-3.0); the PyPI package piper-tts is MIT only up to 1.2.0. Every Piper loads libespeak-ng (GPL-3.0-or-later) in its own process, which is why VOX runs it only as a separate program. Each Piper voice has its own licence card; read it. All pretrained openWakeWord models are CC BY-NC-SA 4.0 (non-commercial), so VOX ships no wake word, only a hook for models you bring. Full steps: docs/08-INSTALLATION.md, "Local speech".
With whisper.cpp, recognition starts when the caller stops talking, so answers come later; keep barge-in on vad. Local speech has Unverified on a real call.