Skip to main content

Railway Environment Variables

Set these in Railway for each environment. Never commit real values to the repo.

Domain Configuration — HWORK_DOMAIN

HWORK_DOMAIN is the bare email/CORS domain. It controls Specialist email addresses ({firstname}`@`{HWORK_DOMAIN}), transactional email from-address (onboarding@{HWORK_DOMAIN}), and the wildcard CORS allowlist (`*.`{HWORK_DOMAIN}).

ServiceVariableDev valueStaging valueProduction value
APIHWORK_DOMAINdev.h852.workh852.workh.work
FrontendNEXT_PUBLIC_HWORK_DOMAINdev.h852.workh852.workh.work

Replaces: SPECIALIST_EMAIL_DOMAIN and NEXT_PUBLIC_APP_DOMAIN (both deprecated — remove from any env where they appear).

App URL — APP_BASE_URL

APP_BASE_URL is the public URL of the user-facing app, used in outbound user-facing replies (WhatsApp pairing prompts, etc.). It is intentionally separate from HWORK_DOMAIN because the app and the email domain are not always the same host:

ServiceVariableDev valueStaging valueProduction value
APIAPP_BASE_URLhttps://dev.h852.workhttps://staging.h852.workhttps://app.h.work

Why: dev runs the entire app on dev.h852.work (no app. prefix), while staging/prod use app.{HWORK_DOMAIN}``. Constructing URLs from HWORK_DOMAIN alone would produce https://app.dev.h852.work for dev, which doesn't exist.


Canonical environment selector — APP_ENV

APP_ENV is the canonical environment-name source for all backend services. NODE_ENV is always production in any deployed Node build, so it cannot distinguish staging from production.

ServiceVariableAcceptable valuesNotes
API (api/)APP_ENVdevelopment | staging | productionRead by api/src/common/env.ts (IS_PRODUCTION, IS_STAGING, IS_DEV, IS_DEPLOYED).
Frontend (Vercel)NEXT_PUBLIC_APP_ENVsame valuesFrontend mirror — Next.js requires the NEXT_PUBLIC_ prefix to expose env to the browser bundle. Read by frontend/src/lib/env.ts.
Agent (agent/)APP_ENVsame valuesSame convention.

Set this per Railway environment (and per Vercel environment) — never in code. The hard-delete-org endpoint and other dev-only paths are gated on IS_PRODUCTION === false.


WhatsApp / Twilio

One inbound WhatsApp number per environment. Set WHATSAPP_PHONE_NUMBER to the E.164 number for that env. Leave it unset on environments that don't have a number provisioned yet — the UI will show "not configured" and inbound routing will be inert.

VariableDevStagingProduction
WHATSAPP_PHONE_NUMBER+18566992888(unset — not provisioned)(unset — not provisioned)
TWILIO_WEBHOOK_BASE_URLhttps://api-dev-b36f.up.railway.apphttps://api-staging-bd3f.up.railway.apphttps://api-production-f95e.up.railway.app
TWILIO_ACCOUNT_SIDACb23672b636fa0d15522099ec5119bb3f (main)same (main)same (main)
TWILIO_API_KEY / TWILIO_API_SECRET(per env)(per env)(per env)
TWILIO_AUTH_TOKENmain account auth token (preferred for sig verify)samesame
TWILIO_SUBACCOUNT_SID / TWILIO_SUBACCOUNT_TOKENoptional — fallback for subaccount-owned senderssamesame

The Twilio webhook for each WhatsApp sender must be set in the Twilio Console (Messaging → Senders) to ${TWILIO_WEBHOOK_BASE_URL}/channels/whatsapp/inbound. The dev sender (+18566992888, SID XE5d4de7ff487f3fd6efc37e970205f8f9) currently points at the dev API.

Legacy data: The whatsapp_number_pool table still exists for back-compat with old tests, but routing reads WHATSAPP_PHONE_NUMBER directly. New senders are linked to orgs via pairing codes (whatsapp_linked_numbers).