C Concierge Documentation
Docs / Start here / Configuration
Reference

Configuration & secrets.

Every environment variable, secret, binding, OAuth redirect, webhook URL, and cron trigger Concierge expects. All sensitive values are stored as Cloudflare Workers secrets via wrangler secret put; non‑sensitive values live in the [vars] block of wrangler.toml.

Total secrets 14 required · 4 optional Env vars 8 Bindings 6
i
Setting secrets

Use the Wrangler CLI: wrangler secret put SECRET_NAME. You’ll be prompted to enter the value securely. Secrets are encrypted at rest by Cloudflare and exposed to your Worker as environment variables.

Environment variables

The Worker reads three groups of plain‑text vars: a static default in wrangler.toml, deployment‑specific values injected at deploy time, and optional integration overrides set in the dashboard.

Staticwrangler.toml · [vars]
VariableDescription
ENVIRONMENTproduction or development. Dev mode bypasses some auth checks.
Per‑deploymentWorkers › Settings › Builds › Build environment variables
VariableDescription
CF_ACCESS_TEAMCloudflare Access team name (the subdomain in myteam.cloudflareaccess.com).
CF_ACCESS_AUDCloudflare Access Application Audience (AUD) tag. Configure the Access app with the path your-domain/manage*: no slash before the asterisk, so it covers both the /manage dashboard and every subroute.
PUBLIC_BASE_URLYour deployment’s primary URL, e.g. https://concierge.example.com. Used in approval‑digest email links and the email‑domain redirect.
EMAIL_DOMAINEmail domain you control; configure MX records manually on its apex and wire it into Cloudflare Email Routing.
WHATSAPP_WABA_IDYour WhatsApp Business Account ID. Only required if you’re running WhatsApp.
WHATSAPP_SIGNUP_CONFIG_IDMeta Embedded Signup configuration ID for your Meta app. Only required if you’re running WhatsApp.

The npm deploy script reads these from process.env and passes them as --var flags. Build env vars persist across deploys; Worker plain‑text vars set in the dashboard get wiped on each deploy.

Optional overridesWorkers › Settings › Variables (dashboard)
VariableDescription
AI_MODELWorkers AI reply model. Default @cf/meta/llama-4-scout-17b-16e-instruct.
AI_FAST_MODELFast classifier model (prompt‑injection scan + persona safety check). Default @cf/meta/llama-3.1-8b-instruct-fast.

Secrets

Sensitive values, set with wrangler secret put. Grouped by integration.

Core

Encryptionrequired for token storage 1 secret
SecretDescription
ENCRYPTION_KEY 32‑byte hex key for AES‑256‑GCM encryption of stored tokens. Generate with openssl rand -hex 32.

Google OAuth

Sign-inused by /auth/login 2 secrets
SecretDescription
GOOGLE_OAUTH_CLIENT_IDGoogle OAuth client ID (for sign-in).
GOOGLE_OAUTH_CLIENT_SECRETGoogle OAuth client secret.

Meta: Facebook / Instagram / WhatsApp

Meta Graph APIshared app for FB Login, Instagram, WhatsApp signup 5 secrets
SecretDescription
META_APP_IDMeta app ID: shared for Facebook Login, Instagram, WhatsApp signup.
META_APP_SECRETMeta app secret: used for webhook signature verification and token exchange.
WHATSAPP_ACCESS_TOKENSystem user token for your shared WABA.
WHATSAPP_VERIFY_TOKENWebhook verification token. Generate with openssl rand -hex 16.
INSTAGRAM_VERIFY_TOKENInstagram webhook verification token. Generate with openssl rand -hex 16.

Discord

Interactions APIslash commands, button & modal callbacks 3 secrets
SecretDescription
DISCORD_PUBLIC_KEYDiscord application public key (Ed25519 signature verification for interactions).
DISCORD_APPLICATION_IDDiscord application ID (for registering slash commands).
DISCORD_BOT_TOKENDiscord bot token (for sending messages and managing interactions).

Razorpay optional

Paymentscredit packs & subscription billing 3 secrets
SecretDescription
RAZORPAY_KEY_IDRazorpay API key ID.
RAZORPAY_KEY_SECRETRazorpay API key secret.
RAZORPAY_WEBHOOK_SECRETRazorpay webhook secret (for verifying payment notifications at POST /webhook/razorpay).

Bindings

Cloudflare resource bindings declared in wrangler.toml. The IDs come from the deployment guide.

wrangler.tomlresource bindings 6 bindings
BindingTypeDescription
DBD1SQLite database for message logs, payments, audit, credit packs.
KVKVConfig store for tenants, accounts, sessions, routing rules, billing state, persona.
AIAICloudflare Workers AI for reply generation, prompt‑injection scanning, persona safety classification, and BGE embeddings.
EMAILsend_emailOutbound email for forwarding and reverse‑alias replies.
REPLY_BUFFERDOPer‑conversation reply buffer that batches multi‑message bursts within wait_seconds.
SAFETY_QUEUEQueuePersona safety classifier jobs. Producer + consumer bound on the same Worker.

Cloudflare Queues

Two queues must exist before deploy:

  • concierge-safety: producer + consumer for the persona safety classifier. The Worker enqueues a SafetyJob on each persona save and consumes the same queue via #[event(queue)].
  • concierge-safety-dlq: dead‑letter queue for safety jobs after 3 retries. Inspect failures here when persona checks stop completing.
bash terminal
wrangler queues create concierge-safety
wrangler queues create concierge-safety-dlq

Local wrangler dev works without queues configured: safety_queue::enqueue logs and falls through, leaving personas in Pending until you deploy against a properly bound environment.

Locale & supported languages

Every tenant has a BCP‑47 locale tag (Tenant.locale) plus an independent currency override. Currently shipped: en-IN (default; Indian‑style number grouping, INR currency) and en-US (Western grouping, USD). Locale is set at signup from the request’s Accept-Language header, falling back to cf-ipcountry, then to en-IN. Admins can change both locale and currency independently from /dashboard/settings.

Adding a new locale is a drop‑in change: place a translated messages.ftl at assets/locales/{tag}/, register the tag in src/i18n.rs::Translator::new and src/locale.rs::Locale::from_request, then rebuild. CLDR data for number / currency formatting ships automatically via icu4x’s compiled_data feature. AI‑generated reply content stays English regardless of the UI locale.

Conversation timing

Three per‑tenant knobs control how the worker treats a customer thread. They live on OnboardingState.conversation (a ConversationConfig) in KV at onboarding:{tenant}; each is Option<u32> and falls back to a constant in src/prompt.rs when unset. Tenants edit them from the Conversation Timing card on /dashboard/settings; PUT /dashboard/settings/conversation validates bounds and the cross‑field invariant before persisting.

ConversationConfigper‑tenant runtime knobs 3 knobs
FieldBoundsDefaultEffect
idle_gap_mins 5…1440 mins 360 (6 h) — DEFAULT_CONVERSATION_IDLE_GAP_MINS After this much customer silence, the next inbound starts a fresh conversation: history clears, handoff state is wiped, the persona replies normally.
handoff_cooldown_mins 5…1440 mins 60 — DEFAULT_HANDOFF_COOLDOWN_MINS After the model emits [[HANDOFF]], replies switch to the holding‑pattern voice for this long; past it, the worker stays silent.
max_history_messages 1…200 turns 20 — DEFAULT_CONVERSATION_MAX_MESSAGES Cap on the number of recent (role, content) turns passed to the reply model on each AI call.

Cross‑field invariant: idle_gap_mins > handoff_cooldown_mins. The form rejects saves that would let an active handoff be wiped before its cooldown ended.

OAuth redirect URIs

Register these in the respective developer consoles:

  • Google: https://your-domain/auth/callback
  • Facebook: https://your-domain/auth/facebook/callback and https://your-domain/instagram/callback

Webhook URLs

Configure these in the respective platforms:

PlatformURLMethod
WhatsApphttps://your-domain/webhook/whatsappGET / POST
Instagramhttps://your-domain/webhook/instagramGET / POST
Discordhttps://your-domain/discord/interactionsPOST
Razorpayhttps://your-domain/webhook/razorpayPOST

Cron triggers

A daily cron runs at 0 6 * * * (06:00 UTC) for Instagram token refresh. Configured in the [triggers] section of wrangler.toml.

toml wrangler.toml · partial
# Identity
name = "concierge"
main = "worker-shim.mjs"
compatibility_date = "2024-01-01"

# Plain-text variables (deployment-specific values come in via --var at deploy time)
[vars]
ENVIRONMENT           = "production"

# Bindings
[[d1_databases]]
binding      = "DB"
database_name = "concierge"
database_id   = "…"

[[kv_namespaces]]
binding = "KV"
id      = "…"

[ai]            binding = "AI"
[[send_email]]   name    = "EMAIL"

[[durable_objects.bindings]]
name       = "REPLY_BUFFER"
class_name = "ReplyBufferDO"

# Persona safety classifier
[[queues.producers]]
queue   = "concierge-safety"
binding = "SAFETY_QUEUE"

[[queues.consumers]]
queue             = "concierge-safety"
max_batch_size    = 10
max_batch_timeout = 5
max_retries       = 3
dead_letter_queue = "concierge-safety-dlq"

# Daily Instagram token refresh, 06:00 UTC
[triggers]
crons = ["0 6 * * *"]
!
Rotating secrets

Rotating ENCRYPTION_KEY invalidates every encrypted Instagram page token in KV: customers will need to reconnect. Webhook verify tokens (WHATSAPP_VERIFY_TOKEN, INSTAGRAM_VERIFY_TOKEN) can be rotated independently; just update them in the Meta Developer Console at the same time.