Clinic Portal — Staging / Production Environments¶
| Branch | Railway environment | Service | Supabase |
|---|---|---|---|
main |
production | clinic-theonset-app |
Onset (emxrmjysvxeboavhatry) |
develop |
staging | clinic-theonset-app-staging |
Onset-staging (lzpyfohgfdohrtlamzek) |
Production: https://clinic.theonset.app
Staging: https://clinic-theonset-app-staging-staging.up.railway.app
Railway staging service¶
Project: disciplined-beauty → environment staging → service clinic-theonset-app-staging
- Connected to GitHub repo
Last-1-Enterprises/clinic.theonset.app - Source branch:
develop— connect in Railway → service → Settings → Source (one-time; initial deploy usedrailway up) - Dockerfile build (
railway.toml)
Staging env vars (set via Railway CLI or dashboard)¶
| Variable | Notes |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
https://lzpyfohgfdohrtlamzek.supabase.co |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
staging anon JWT |
SUPABASE_SERVICE_ROLE_KEY |
staging service-role JWT |
NEXT_PUBLIC_APP_URL |
https://clinic-theonset-app-staging-staging.up.railway.app |
NEXT_PUBLIC_ONSET_APP_URL |
https://develop.onset-web.pages.dev or https://theonset.app |
PATIENT_PII_ENCRYPTION_KEY |
Separate from prod (base64url, 32 bytes) |
STRIPE_SECRET_KEY / NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Stripe test keys only |
STRIPE_SOLO_MONTHLY_PRICE_ID / STRIPE_SOLO_ANNUAL_PRICE_ID |
Solo $59/mo, $499/yr — run npm run stripe:pricing (test key) to mint |
STRIPE_PRACTICE_MONTHLY_PRICE_ID / STRIPE_PRACTICE_ANNUAL_PRICE_ID |
Practice $129/mo, $1,090/yr |
STRIPE_PRACTICE_SEAT_MONTHLY_PRICE_ID / STRIPE_PRACTICE_SEAT_ANNUAL_PRICE_ID |
Per-seat $39/mo, $390/yr (licensed quantity) |
STRIPE_FULLSCRIPT_ADDON_MONTHLY_PRICE_ID / STRIPE_FULLSCRIPT_ADDON_ANNUAL_PRICE_ID |
Fullscript add-on for Solo $29/mo, $290/yr |
STRIPE_TRIAL_DAYS |
14 — card-required trial granted on first Practice checkout |
STRIPE_WEBHOOK_SECRET |
Test webhook we_1Tjn5pJBZ5q5XOPpd48kyyk6 (set in Railway only) |
NOTIFY_FUNCTION_SECRET |
Staging-specific; must match staging Supabase edge function secret |
CLINIC_SUPPORT_INBOX_EMAIL |
Optional; default ryan@theonset.app — portal Help tool email destination |
SENDGRID_API_KEY |
Optional on Railway; required for portal Help emails with attachments (otherwise tickets save but email fails) |
PROTOCOL_IMPORT_SECRET |
Staging-specific |
CRON_SECRET |
Staging-specific |
FULLSCRIPT_CLIENT_ID / FULLSCRIPT_CLIENT_SECRET |
Fullscript sandbox OAuth app |
FULLSCRIPT_API_BASE |
https://api-us-snd.fullscript.io for sandbox |
FULLSCRIPT_WEBHOOK_SECRET |
Webhook HMAC secret from Fullscript dashboard |
FULLSCRIPT_TOKEN_ENCRYPTION_KEY |
Optional; base64url 32-byte key (or reuse PATIENT_PII_ENCRYPTION_KEY) |
FULLSCRIPT_INTEGRATION_ENABLED |
Staging: true. Production: false until compliance |
FULLSCRIPT_CUSTOMER_AVAILABLE |
Staging: true for sandbox POC. Production: false |
FULLSCRIPT_BILLING_ENABLED |
false until launch (blocks Solo add-on checkout) |
FULLSCRIPT_EMBED_PUBLIC_KEY / FULLSCRIPT_EMBED_ENV |
Optional; enables in-portal embed on patient profile |
CLINIC_REQUIRE_OWNER_MFA |
Optional; true to require owner TOTP (enable Supabase MFA first). Default unset = enrollment available, not forced |
Auth session TTLs (Supabase Dashboard)¶
Set on both staging (lzpyfohgfdohrtlamzek) and prod (emxrmjysvxeboavhatry):
| Setting | Recommended | Where |
|---|---|---|
| JWT expiry (access token) | 3600 (1 hour) |
Auth → Settings → JWT expiry |
| Refresh token reuse / rotation | Enabled | Auth → Settings |
| Refresh token lifetime | ≤ 7 days (604800s) if the dashboard exposes it; otherwise document current value | Auth → Settings |
Shorter refresh TTL is a dashboard change, not an app deploy. Coordinate with MFA rollout so owners are not locked out mid-session.
MFA (owners)¶
- Supabase Dashboard → Auth → MFA → enable TOTP
- Owners enroll at Settings → Security
- Optional hard gate: set
CLINIC_REQUIRE_OWNER_MFA=trueon Railway after enrollment is verified on staging
Do not copy production live Stripe keys or prod PII encryption key to staging.
See FULLSCRIPT_INTEGRATION.md for OAuth redirect URI, webhook URL, and E2E checklist.
See GOOGLE_SIGNIN.md for Google OAuth setup (Google Cloud + Supabase providers on staging and prod). Includes OAuth consent screen branding (docs/assets/google-oauth-app-logo.png). No Railway env vars — credentials are configured in Supabase Auth only.
Staging patient invite emails¶
Patient invites (Add Patient, quickstart, resend invite) call the
practitioner-notifications Supabase Edge Function unless SENDGRID_API_KEY is set
on Railway. Staging requires a one-time edge function deploy + secrets sync.
Symptom: patient saves but UI shows "email could not be delivered" — usually
404 (function not deployed) or 500 (missing SENDGRID_API_KEY on staging Supabase).
One-time setup:
cd clinic.theonset.app
cp supabase/.env.staging.example supabase/.env.staging
# Paste SENDGRID_API_KEY (same key as prod Supabase Edge secrets / SendGrid dashboard)
chmod +x scripts/supabase/deploy-staging-notifications.sh
./scripts/supabase/deploy-staging-notifications.sh
The script reads NOTIFY_FUNCTION_SECRET and PATIENT_PII_ENCRYPTION_KEY from
Railway staging and mirrors them onto staging Supabase along with SendGrid.
Auth signup emails (clinic portal account confirmation) use Supabase Auth templates. Point staging Auth at the staging portal URL:
SUPABASE_ACCESS_TOKEN=sbp_xxx \
PROJECT_REF=lzpyfohgfdohrtlamzek \
SITE_URL=https://clinic-theonset-app-staging-staging.up.railway.app \
URI_ALLOW_LIST="https://clinic-theonset-app-staging-staging.up.railway.app/auth/callback,http://localhost:3000/auth/callback" \
./scripts/supabase/apply-auth-email-templates.sh
Staging email test checklist
- Add Patient → invite email with connection code (+ optional clinic code block)
- Patient detail → Resend invite
- Assign protocol to invited patient → protocol assigned email (edge function)
- (Optional) Weekly summary cron →
/api/cron/weekly-summarywith stagingCRON_SECRET
Stripe test mode (configured)¶
Product: Onset Clinic Pro (Staging) (prod_UjFbg8neAbAUGC)
| Plan | Test price ID | Amount |
|---|---|---|
| Monthly | price_1Tjn5oJBZ5q5XOPpozzGJP5E |
$49/mo |
| Annual | price_1Tjn5oJBZ5q5XOPpLovS80bh |
$399/yr |
Webhook: https://clinic-theonset-app-staging-staging.up.railway.app/api/stripe/webhook
Events: checkout.session.completed, customer.subscription.updated,
customer.subscription.deleted, customer.subscription.trial_will_end,
invoice.payment_failed.
Use Stripe test card 4242 4242 4242 4242 with any future expiry/CVC at checkout.
CLI helpers¶
railway environment staging
railway service link clinic-theonset-app-staging
railway variables # inspect
railway logs
railway redeploy
Promotion flow¶
Supabase migrations: staging first, then prod (see Onset/docs/ENVIRONMENTS.md).