reCAPTCHA & Outbound Email
The public chat page has no login, so AVA protects it with Google reCAPTCHA
v3 (invisible scoring — clients never solve a puzzle) plus server-side rate
limiting. Outbound transactional email goes through SMTP2GO. This page
covers both setups and the fail-closed rules that govern them.
Google reCAPTCHA v3
Create the site
At the Google reCAPTCHA admin console,
create a v3 site for your public chat hostname (e.g. chat.example.com).
Add localhost to the site's domains if you also want local development to
verify against the real service. Collect the two keys:
| Value | Maps to | Exposure |
|---|---|---|
| Site key | NEXT_PUBLIC_RECAPTCHA_SITE_KEY | Public by design — baked into the browser bundle at build time (a Docker build arg; changing it requires a rebuild) |
| Secret key | RECAPTCHA_SECRET_KEY | Secret — server-side verification only. Required to boot. |
How verification behaves
- Every chat-start (and the email-fallback form) submission carries a
reCAPTCHA token that the server verifies against Google'ssiteverify
endpoint before doing anything else. - Verification is score-based and rejects low-scoring (bot-like) tokens,
tokens minted for a different action, and tokens minted on a different
hostname. - The outbound call to Google has its own timeout (
RECAPTCHA_TIMEOUT_MS,
default10000ms), so a hung Google endpoint cannot stall requests. - Verification is fail-closed: a missing secret, a network error reaching
Google, or an explicit failure from Google all reject the request. A Google
outage cannot silently disable bot protection.
RECAPTCHA_DEV_BYPASS — development only, refused in production
RECAPTCHA_DEV_BYPASS — development only, refused in productionFor local development or CI where you don't want to configure real keys:
| Variable | Default | Effect |
|---|---|---|
RECAPTCHA_DEV_BYPASS | (unset — fail closed everywhere) | Set to the exact string true to fail open when verification can't complete (missing key or network error). Honored only when NODE_ENV is not production. |
Two hard properties worth knowing:
- It is refused outright in production. When
NODE_ENV=production, the
bypass is ignored no matter what the variable says — no runtime
configuration can disable bot protection on a production deployment. - The match is exact:
1,yes,TRUEetc. do not activate it.
Since local development typically has a real RECAPTCHA_SECRET_KEY anyway
(with localhost on the site's domain list), most deployments never need this
variable at all.
SMTP2GO — outbound email
AVA sends three kinds of transactional mail:
| Trigger | Recipient variable | |
|---|---|---|
| Support-request fallback | Client uses the "email us instead" path (after hours, or after a queue timeout) | SUPPORT_FALLBACK_EMAIL |
| Engineer feedback | An engineer submits feedback from the dashboard | FEEDBACK_EMAIL |
| Weekly survey report | The weekly CSAT report endpoint runs (guarded by WEEKLY_REPORT_TOKEN if you wire up a cron) | REPORTS_EMAIL |
Account setup
Create an SMTP2GO account and an SMTP user under it (Settings → SMTP
Users), and verify your sending domain so mail from your SMTP2GO_FROM
address passes SPF/DKIM. Then set:
| Variable | Default | Required | Effect |
|---|---|---|---|
SMTP2GO_HOST | mail.smtp2go.com | Optional | SMTP host |
SMTP2GO_PORT | 2525 | Optional | SMTP port |
SMTP2GO_USER | — | Required to boot | SMTP username |
SMTP2GO_PASSWORD | — | Required to boot | SMTP password |
SMTP2GO_FROM | — | Required for any mail to send (fail-closed) | Sender address on all outbound mail, e.g. [email protected] |
SMTP_TIMEOUT_MS | 10000 | Optional | SMTP connect + socket timeout |
SMTP2GO_FROMis fail-closed. There is no built-in sender address: if
SMTP2GO_FROMis unset or empty, every email route refuses to send — the
attempt is logged as an error and the request returns a clean failure
instead of mailing from a made-up address. Set it to an address on a domain
your SMTP2GO account is authorized to send from.
While the SMTP transport is SMTP2GO-shaped by default, the host/port/user/
password are plain SMTP settings — another SMTP relay that accepts
username/password auth on these variables will also work.
Recipient configuration (fail-closed contract)
None of the recipient addresses have built-in defaults — a deployment can
never silently send mail to an address you didn't configure:
| Variable | Required | Behavior when unset |
|---|---|---|
SUPPORT_FALLBACK_EMAIL | Required to boot — the container refuses to start without it | n/a (boot-validated, so the client-facing email fallback always has a real destination) |
FEEDBACK_EMAIL | Optional | The feedback send is skipped and an error is logged — nothing is sent anywhere |
REPORTS_EMAIL | Optional | The report send is skipped and an error is logged — nothing is sent anywhere |
SUPPORT_FALLBACK_EMAIL is boot-required because it backs a client-facing
promise: when chat is unavailable, the "email us a ticket" form must actually
deliver somewhere. The other two are internal conveniences and simply stay off
until you configure them.
Environment variable summary
| Variable | Required | Notes |
|---|---|---|
NEXT_PUBLIC_RECAPTCHA_SITE_KEY | Required at build time | Public site key, baked into the client bundle |
RECAPTCHA_SECRET_KEY | Required to boot | Server-side verification secret |
RECAPTCHA_DEV_BYPASS | Optional, dev-only | Exact true; ignored in production |
RECAPTCHA_TIMEOUT_MS | Optional (10000) | siteverify fetch timeout |
SMTP2GO_HOST / SMTP2GO_PORT | Optional | Defaults mail.smtp2go.com / 2525 |
SMTP2GO_USER / SMTP2GO_PASSWORD | Required to boot | SMTP credentials |
SMTP2GO_FROM | Required for sending (fail-closed) | Sender address |
SUPPORT_FALLBACK_EMAIL | Required to boot | Client email-fallback recipient |
FEEDBACK_EMAIL | Optional (fail-closed) | Engineer feedback recipient |
REPORTS_EMAIL | Optional (fail-closed) | Weekly survey report recipient |
WEEKLY_REPORT_TOKEN | Optional | Bearer token for the weekly-report cron endpoint (openssl rand -hex 32); only needed if you schedule that report |
SMTP_TIMEOUT_MS | Optional (10000) | SMTP connection/socket timeout |
See configuration.md for the complete configuration
reference.
Next steps
quickstart.md— these values are set in the credential
step of the bring-up.white-label.md— the rest of the deployment identity
(company name, phone, portal link) that appears alongside these email
touchpoints.
Updated about 1 hour ago