FreshBay Health (KAI-OSCAR) Integration Plan¶
First real client deployment: Per-clinic credential isolation, OAuth 1.0a token exchange, TLS hardening, and phone search graceful degradation.
Status: E2E VALIDATED (updated 2026-03-07 with full booking flow validation) Date: February 25, 2026 (updated March 7, 2026) Risk Level: HIGH — first production client, multi-tenant credential isolation is foundational Constraint: Dev OSCAR must continue working through every phase (no regression)
Critical Update: SOAP is Non-Viable for Kai-Hosted OSCAR
Investigation on 2026-02-26 proved that Cloudflare WAF blocks all SOAP requests to Kai-hosted OSCAR data services. The WAF uses content-inspection: Content-Type: text/xml + <soapenv:Envelope> body triggers HTTP 403. Only OAuth 1.0a REST (/ws/services/*) passes through CF. This requires the preferRest protocol extension and ModuleNames=REST enabled on Kai's side. See OSCAR API Protocol Analysis for the 8-test proof matrix, per-method routing table, and implementation plan.
E2E Booking Flow Validation (2026-03-02)¶
All 5 core booking operations validated against FreshBay Kai OSCAR:
| # | Operation | Status | Details |
|---|---|---|---|
| 1 | searchPatient | PASS | quickSearch with ?query= (confirmed correct param) |
| 2 | getScheduleSlots | PASS | Returns data: {providerId, date, holiday, slots[]} |
| 3 | createAppointment | PASS | NewAppointmentTo1 format, startTime: "HH:mm", status: "t" required |
| 4 | getAppointments | PASS | Filter by provider + date range |
| 5 | cancelAppointment | PASS | Uses /appointment/{id}/updateStatus (not /updateAppointment) |
Test appointment: ID 314132, created and cancelled on provider 124 (2026-03-19 slot).
Key Findings¶
statusfield is REQUIRED forschedule/add(omitting causes 500)startTimemust beHH:mmformat (use.slice(0, 5), NOT.replace(/:\d{2}$/, ''))- Off-grid times (e.g., 15:15) cause OSCAR NPE --> 500
- quickSearch phone numbers NOT indexed in Kai (use name search instead)
- OAuth consumer
Vitaradevre-authorized 2026-03-03, TTL=-1 (permanent)
1. CURRENT STATE¶
FreshBay Health is a BC clinic using a KAI-managed OSCAR instance at https://fbh.kai-oscar.com/oscar/. This is our first real client deployment — the platform was previously tested only against the dev OSCAR at 15.222.50.48.
Current Architecture (Single-Tenant)¶
VOICE CALL (Vapi)
│
▼
┌──────────────────────────────────────────────────────────────┐
│ vapi-webhook.ts → booking.service → EmrAdapterFactory │
│ │
│ EmrAdapterFactory resolves adapter per clinic: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ SOAP username/password → GLOBAL env vars │ │
│ │ ⚠ OSCAR_SOAP_USERNAME / OSCAR_SOAP_PASSWORD │ │
│ │ ⚠ Same creds for ALL clinics │ │
│ │ │ │
│ │ OAuth consumer key → GLOBAL env var │ │
│ │ ⚠ OSCAR_OAUTH_CONSUMER_KEY (not per-clinic) │ │
│ │ │ │
│ │ TLS → NODE_TLS_REJECT_UNAUTHORIZED=0 │ │
│ │ ⚠ Bypasses ALL SSL globally (even KAI valid certs) │ │
│ │ │ │
│ │ admissionProgramId → hardcoded 10034 │ │
│ │ ⚠ May differ on FreshBay OSCAR │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ Phone search → REST bridge fallback │
│ ⚠ KAI has NO bridge — phone search will fail silently │
│ │
│ OAuth tokens → no TTL tracking │
│ ⚠ 350,000s (~4 day) expiry — silent auth failure │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ OscarSoapAdapter │
│ → getSoapOptions(): rejectUnauthorized: false (hardcoded) │
│ → createPatient(): admissionProgramId: 10034 (hardcoded) │
│ → searchPatientByPhone(): bridge-only (no SOAP fallback) │
│ → constructor: username/password from global env │
└──────────────────────────────────────────────────────────────┘
Critical Gaps¶
| # | Gap | Risk | Phase |
|---|---|---|---|
| 1 | SOAP username/password are global env vars, not per-clinic | Blocks multi-clinic | 1 |
| 2 | OAuth consumer key resolution uses global env, not per-clinic DB | Blocks FreshBay creds | 1 |
| 3 | NODE_TLS_REJECT_UNAUTHORIZED=0 bypasses ALL SSL — KAI has valid certs |
Security risk | 1 |
| 4 | No OAuth 1.0a token exchange tooling — need access tokens | Blocks OAuth REST | 2 |
| 5 | Phone search falls back to bridge — KAI has no bridge | Feature gap | 3 |
| 6 | admissionProgramId: 10034 hardcoded — FreshBay may differ |
Breaks patient reg | 1 |
| 7 | No token TTL tracking — 350,000s (~4 day) expiry | Silent auth failure | 2 |
FreshBay Credentials (Available)¶
| Credential | Value | Notes |
|---|---|---|
| OSCAR URL | https://fbh.kai-oscar.com/oscar/ |
KAI-managed, valid CA certs |
| OAuth Consumer Key | nyiwc1wpazjqy9pd |
From OSCAR ServiceClient |
| OAuth Consumer Secret | fqxslbrd7o2bt5pm |
From OSCAR ServiceClient |
| OAuth Token TTL | 350,000s (~4 days) | Requires periodic re-auth |
| SOAP admin username | To be provided | For WS-Security auth |
| SOAP admin password | To be provided | For WS-Security auth |
| Callback URI | https://api.vitaravox.ca/api/oscar-oauth/callback |
We control ServiceClient registration |
2. FUTURE STATE¶
Target Architecture (Multi-Tenant)¶
VOICE CALL (Vapi)
│
▼
┌──────────────────────────────────────────────────────────────┐
│ vapi-webhook.ts → booking.service → EmrAdapterFactory │
│ │
│ EmrAdapterFactory resolves PER-CLINIC: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ SOAP username → clinic_config.oscar_soap_username │ │
│ │ SOAP password → clinic_config.oscar_soap_password_enc │ │
│ │ OAuth consumer key → clinic_config.oscar_oauth_* │ │
│ │ OAuth token key/secret → clinic_config (encrypted) │ │
│ │ TLS verify → clinic_config.oscar_tls_verify │ │
│ │ admissionProgramId → clinic_config │ │
│ │ ✓ Env vars are FALLBACK only (backward compat) │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ Phone search → multi-strategy: │
│ 1. SOAP searchDemographicByName (post-filter by phone) │
│ 2. Bridge (if configured, dev only) │
│ 3. Empty array + log (graceful degradation) │
│ │
│ OAuth token TTL → monitored │
│ ✓ 12h scheduled check, warn 48h before expiry │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ OscarSoapAdapter │
│ → getSoapOptions(): rejectUnauthorized from this.tlsVerify │
│ → createPatient(): admissionProgramId from this.config │
│ → searchPatientByPhone(): SOAP-first, bridge fallback │
│ → constructor: username/password from per-clinic DB │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ OAuth Token Exchange (NEW) │
│ → CLI script: initiates 3-legged OAuth flow │
│ → Callback endpoint: auto-completes token exchange │
│ → Stores encrypted tokens + expiry in ClinicConfig │
│ → Scheduled monitor: warns 48h before token expiry │
└──────────────────────────────────────────────────────────────┘
3. MIGRATION SAFETY STRATEGY¶
FRESHBAY INTEGRATION -- Phase Dependencies
Phase 1: Schema + Adapter ─────────────┐
(per-clinic creds, TLS toggle, │
AES-256-GCM encryption, env │
fallback for backward compat) │
│
Phase 2: OAuth 1.0a Token Exchange ────┤
(3-legged flow, token storage, │
TTL tracking, 12h expiry monitor) │
│
├──► Phase 5: Connection Testing
│ + OAuth Flow
Phase 3: Phone Search Without Bridge ──┤ (SOAP health check,
(SOAP searchByName fallback, │ token exchange,
graceful degradation to │ patient reg test)
name-based identification) │ │
│ ▼
Phase 4: FreshBay Clinic DB Setup ─────┘ Phase 6: Onboarding
(clinic record, encrypted + Validation Updates
config, dev TLS override) (testEMRConnection,
encryption checks,
token expiry check)
│
▼
Phase 7: Production
Deploy + Hardening
(migrate, build,
PM2 restart,
remove TLS bypass)
Phase 1: Schema + Adapter Refactoring (Foundation)¶
Goal: Per-clinic credential isolation with env-var fallback for backward compatibility. Safety: Dev OSCAR continues using env vars. No behavior change until FreshBay config is created.
1.1 Prisma Schema Migration¶
File: server/prisma/schema.prisma
Add to ClinicConfig model:
oscarSoapUsername String? @map("oscar_soap_username")
oscarSoapPasswordEnc String? @map("oscar_soap_password_enc")
oscarOauthConsumerKey String? @map("oscar_oauth_consumer_key")
oscarOauthConsumerSecretEnc String? @map("oscar_oauth_consumer_secret_enc")
oscarOauthTokenKey String? @map("oscar_oauth_token_key")
oscarOauthTokenSecretEnc String? @map("oscar_oauth_token_secret_enc")
oscarOauthTokenExpiresAt DateTime? @map("oscar_oauth_token_expires_at")
oscarTlsVerify Boolean @default(true) @map("oscar_tls_verify")
oscarAdmissionProgramId Int? @map("oscar_admission_program_id")
Run: npx prisma migrate dev --name add-per-clinic-oscar-fields
Backward compatibility: All new fields are nullable (except oscarTlsVerify which defaults to true). Existing clinics unaffected.
1.2 Refactor EmrAdapterFactory¶
File: server/src/adapters/EmrAdapterFactory.ts
Current state (lines 51-62): Only selects existing OAuth fields from ClinicConfig.
Changes:
- Select new fields in Prisma query: oscarSoapUsername, oscarSoapPasswordEnc, oscarTlsVerify, oscarAdmissionProgramId, plus existing fields
- resolveOAuthCreds() (line 101): Read per-clinic oscarOauthConsumerKey first, fall back to env
- oscar-soap case (line 141): Resolve soapUsername and soapPassword from per-clinic DB first, then env fallback
- Pass tlsVerify and admissionProgramId from clinic config to adapter options
- Decrypt new encrypted fields using existing decrypt() from lib/crypto.ts
Safety: Fallback to env vars preserves dev OSCAR behavior when per-clinic fields are null.
1.3 Refactor OscarSoapAdapter¶
File: server/src/adapters/OscarSoapAdapter.ts
Changes:
- Add tlsVerify and admissionProgramId to OscarSoapAdapterOptions interface (line 194)
- Store as instance fields in constructor
- getSoapOptions() (line 263): Use this.tlsVerify instead of hardcoded false — { rejectUnauthorized: !this.tlsVerify }
- createPatient() httpsAgent (line 1001): Use this.tlsVerify
- createPatient() body (line 968): Use this.admissionProgramId ?? 10034 (fallback preserves dev behavior)
1.4 Update provider.service.ts¶
File: server/src/services/provider.service.ts
Changes in updateEMRConfig():
- Handle new fields: oscarSoapUsername, oscarSoapPasswordEnc, oscarOauthConsumerKey, etc.
- Encrypt password/secret fields before storage using existing encrypt() from lib/crypto.ts
- Reset oscarConnectionVerified = false when SOAP creds change
- Call invalidateAdapterCache(clinicId) on credential update
Phase 2: OAuth 1.0a Token Exchange¶
Goal: Automated OAuth flow to obtain and store access tokens for FreshBay OSCAR REST API.
2.1 OAuth Token Exchange (CLI + API Callback)¶
We control the OSCAR ServiceClient registration, so we set the callback URI to https://api.vitaravox.ca/api/oscar-oauth/callback.
New file: server/src/scripts/oscar-oauth.ts
CLI script that initiates the 3-legged OAuth flow:
1. POST {oscarUrl}/ws/oauth/initiate — signed with consumer key/secret → returns request token
2. Print authorization URL: {oscarUrl}/ws/oauth/authorize?oauth_token={req_token}
3. User opens URL in browser, logs into OSCAR, approves
4. OSCAR redirects to our callback endpoint with oauth_verifier
5. Callback endpoint auto-completes token exchange and stores tokens
New route: GET /api/oscar-oauth/callback in routes/api.ts
- Receives oauth_token + oauth_verifier from OSCAR redirect
- Completes POST {oscarUrl}/ws/oauth/token exchange automatically
- Encrypts and stores access token key/secret in ClinicConfig
- Sets oscarOauthTokenExpiresAt = now + TTL
- Renders success page or redirects to admin dashboard
Fallback: CLI also accepts manual --verifier flag for cases where callback is unreachable.
Uses existing oauth-1.0a npm package (already a dependency in OscarSoapAdapter).
2.2 Token Expiry Monitoring¶
File: server/src/jobs/scheduler.ts (or new oauth-token-monitor.ts)
Add scheduled check (every 12 hours):
- Query ClinicConfig where oscarOauthTokenExpiresAt < now + 48h
- Log warning via Pino: "FreshBay OAuth token expires in {X} hours"
- Future: create Notification record for admin dashboard alert
2.3 Circuit Breaker Timeout Adjustment¶
File: server/src/adapters/OscarSoapAdapter.ts
- Increase WSDL cold-start circuit breaker timeout from 4s to 8s (KAI is remote, not localhost)
- Keep 4s for individual SOAP call timeouts (still under Vapi's 5s)
- Add WSDL warm-up on adapter creation (fire health check on first create)
Phase 3: Phone Search Without Bridge¶
Goal: Graceful phone search degradation for KAI instances that have no REST bridge.
3.1 SOAP-Based Phone Search¶
File: server/src/adapters/OscarSoapAdapter.ts
Replace searchPatientByPhone() with multi-strategy approach:
1. Strategy 1: Try SOAP searchDemographicByName with cleaned phone digits — some OSCAR versions match phone fields on numeric input. Post-filter results by last 7 digits match.
2. Strategy 2: Fall back to bridge if configured (dev only)
3. Strategy 3: Return empty array with log (no phone search available)
This is a graceful degradation — voice agent will ask for name instead.
3.2 Voice Agent Prompt Note¶
For FreshBay: Patient-ID agent should prioritize name-based identification over phone-based. This is a Vapi prompt configuration issue, not a code change — document for squad setup.
Phase 4: FreshBay Clinic Database Setup¶
4.1 Create Clinic Record¶
Via Prisma Studio or seed script:
- name: 'FreshBay Health', slug: 'freshbay-health'
- timezone: 'America/Vancouver' (BC clinic — confirm with client)
- status: 'pending' (active after onboarding)
4.2 Create ClinicConfig¶
emrType: 'oscar-soap'oscarUrl: 'https://fbh.kai-oscar.com/oscar'oscarSoapUsername: '<admin-username>'— get from clientoscarSoapPasswordEnc: encrypt('<admin-password>')— get from clientoscarOauthConsumerKey: encrypt('nyiwc1wpazjqy9pd')oscarOauthConsumerSecretEnc: encrypt('fqxslbrd7o2bt5pm')oscarTlsVerify: true(KAI has valid CA certs)- Token fields: null (populated after OAuth flow in Phase 5)
4.3 Set Dev Clinic TLS Override¶
Before removing global NODE_TLS_REJECT_UNAUTHORIZED=0:
- Update dev clinic's ClinicConfig: oscarTlsVerify: false
- Verify dev OSCAR still connects with per-clinic TLS control
- Then remove the global env var from .env
Phase 5: Connection Testing + OAuth Flow¶
5.1 SOAP Connectivity Test¶
Test against https://fbh.kai-oscar.com/oscar/ws/ScheduleService?wsdl:
- Health check (WSDL reachable with valid SSL?)
- Provider list (getProviders2(true))
- Schedule template codes
- Patient search (searchDemographicByName with test term)
- Schedule slots for first provider on next weekday
5.2 Run OAuth Token Exchange¶
First, update the OSCAR ServiceClient callback URI to https://api.vitaravox.ca/api/oscar-oauth/callback. Then:
npx tsx src/scripts/oscar-oauth.ts \
--oscar-url https://fbh.kai-oscar.com/oscar \
--consumer-key nyiwc1wpazjqy9pd \
--consumer-secret fqxslbrd7o2bt5pm \
--clinic-id <freshbay-clinic-id>
Script prints authorization URL. User opens in browser, logs in with OSCAR admin, approves. OSCAR redirects to our callback endpoint which auto-stores tokens.
5.3 Test Patient Registration (OAuth REST)¶
After tokens obtained: POST /ws/services/demographics with test patient data.
Discover correct admissionProgramId (may need to try or check OSCAR admin UI).
5.4 Phone Search Probe¶
Test whether searchDemographicByName returns results for phone number input on KAI instance.
Phase 6: Onboarding + Validation Updates¶
6.1 Update testEMRConnection¶
File: server/src/services/provider.service.ts
Add patient search validation to the existing 4-step test:
- Step 5 (new): adapter.searchPatient({ term: 'test' }) — verify patient search works
- Report result count in response (informational, non-fatal)
6.2 Update Credential Encryption Check¶
File: server/src/services/onboarding.service.ts
Pre-launch check #7 (credentials_encrypted) currently checks old field names. Update to also validate new per-clinic fields:
- oscarSoapPasswordEnc is in AES-256-GCM format
- oscarOauthConsumerSecretEnc is in AES-256-GCM format
- oscarOauthTokenSecretEnc is in AES-256-GCM format (if present)
6.3 Add Token Expiry Pre-Launch Check (New Check #11)¶
Add informational check: "OAuth token valid" — verify oscarOauthTokenExpiresAt > now + 24h. Warn if expiring soon or already expired.
6.4 Config Pull Endpoint Compatibility¶
File: server/src/services/oscar-config.service.ts
Verify pullScheduleTemplateCodes() handles KAI OSCAR response shape:
- KAI may return different field names in SOAP response
- Add defensive parsing with fallbacks
- Log raw response for first-time debugging
Phase 7: Production Deploy + Hardening¶
7.1 Build and Deploy¶
cd ~/vitara-platform/admin-dashboard/server
npx prisma migrate deploy # Apply new migration
npx tsc # Type-check gate
pm2 restart vitara-admin-api # Deploy
pm2 logs vitara-admin-api --lines 50 # Watch for startup errors
7.2 CORS Update¶
File: server/ecosystem.config.cjs
If admin dashboard needs to be accessed from a new domain, update CORS_ORIGIN. Current: https://dev.vitaravox.ca.
7.3 Document KAI-OSCAR Learnings¶
Update memory + docs with:
- KAI WSDL response patterns
- Actual admissionProgramId for FreshBay
- Phone search behavior on KAI
- Token TTL management procedure
- Any KAI-specific SOAP quirks discovered
4. IMPACT ANALYSIS¶
Files Modified¶
| File | Risk | Changes |
|---|---|---|
server/prisma/schema.prisma |
LOW | Add 9 nullable fields to ClinicConfig |
server/src/adapters/EmrAdapterFactory.ts |
MEDIUM | Per-clinic cred resolution with env fallback |
server/src/adapters/OscarSoapAdapter.ts |
MEDIUM | TLS toggle, admissionProgramId, phone search, CB timeout |
server/src/services/provider.service.ts |
LOW | updateEMRConfig for new fields, testEMRConnection step 5 |
server/src/services/onboarding.service.ts |
LOW | Updated encryption check, new token expiry check |
server/src/services/oscar-config.service.ts |
LOW | Defensive parsing for KAI responses |
server/src/routes/api.ts |
LOW | New OAuth callback route |
server/.env |
MEDIUM | Remove NODE_TLS_REJECT_UNAUTHORIZED=0 |
server/ecosystem.config.cjs |
LOW | CORS if needed |
Files Created¶
| File | Purpose |
|---|---|
server/src/scripts/oscar-oauth.ts |
OAuth 1.0a 3-legged flow CLI |
| Prisma migration file | add-per-clinic-oscar-fields |
Files Unchanged¶
| File | Reason |
|---|---|
server/src/adapters/IEmrAdapter.ts |
Interface unchanged |
server/src/adapters/OscarBridgeAdapter.ts |
Bridge adapter untouched |
server/src/services/booking.service.ts |
No booking logic changes |
server/src/services/vapi.service.ts |
No webhook logic changes |
| All Vapi GitOps resources | Voice agent prompts unaffected |
5. CRITICAL BEHAVIOR QUESTIONS¶
| # | Question | Impact | Status |
|---|---|---|---|
| 1 | What is FreshBay's admissionProgramId? |
Breaks patient registration if wrong | OPEN — discover via OSCAR admin UI or trial |
| 2 | Does KAI OSCAR accept SOAP phone-digit search? | Determines if phone search works at all | MOOT — SOAP blocked by CF; phone search will use REST |
| 3 | Can we update the existing ServiceClient callback URI, or must we create a new one? | Affects OAuth flow setup | OPEN — we believe we can update |
| 4 | What is FreshBay's timezone? | Affects schedule slot display | CONFIRMED PST (UTC-8), verified via SystemInfoService getServerTime |
| 5 | Does KAI OSCAR SOAP return the same WSDL schema as dev OSCAR? | Parser compatibility | MOOT — SOAP blocked by CF; using OAuth REST instead |
| 6 | Is FreshBay OSCAR's SOAP WS-Security config identical? | Auth will fail if different | MOOT — WS-Security returns 500 on Kai AND CF blocks SOAP |
| 7 | Does FreshBay OSCAR expose all 3 services? | Partial functionality if not | ANSWERED — 14 SOAP + 1 REST (/ws/rs); /ws/services requires ModuleNames=REST |
| 8 | NEW: Will Kai enable ModuleNames=REST? |
Blocks entire integration | PENDING — email to be sent via clinic admin |
| 9 | NEW: REST response format matches dev OSCAR? | Transformation code correctness | PENDING — capture fixtures from both servers and diff |
6. VERIFICATION CHECKLIST¶
- [ ] Prisma migration applies cleanly
- [ ] Dev OSCAR still works with per-clinic TLS=false (no regression)
- [ ] FreshBay SOAP health check passes (WSDL loads, SSL validates)
- [ ] Provider list pulled from FreshBay OSCAR
- [ ] Schedule template codes pulled and classified (bookable vs blocked)
- [ ] Schedule slots returned for at least one provider
- [ ] Patient search by name works
- [ ] OAuth 3-legged flow completes — access tokens stored encrypted
- [ ] Patient registration via OAuth REST works (test patient created + deleted)
- [ ] Phone search gracefully degrades (empty result, no crash)
- [ ] All 7 blocking pre-launch checks pass for FreshBay
- [ ] Token expiry warning fires when tokens near expiration
- [ ]
NODE_TLS_REJECT_UNAUTHORIZED=0removed from .env - [ ] PM2 restart successful, no startup errors
7. RISK REGISTRY¶
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| KAI SOAP schema differs from dev OSCAR | HIGH | MEDIUM | Test WSDL early in Phase 5, add defensive parsing |
| OAuth token expires during business hours | HIGH | MEDIUM | 12h monitor + 48h warning window |
| admissionProgramId wrong → patient reg fails | MEDIUM | HIGH | Discover via OSCAR admin UI before going live |
| Dev OSCAR breaks after TLS refactor | HIGH | LOW | Per-clinic TLS=false for dev, test before removing global |
| KAI network latency exceeds Vapi timeout | MEDIUM | MEDIUM | WSDL cold-start CB at 8s, warm-up on adapter creation |
| OAuth callback unreachable (DNS/firewall) | MEDIUM | LOW | CLI fallback with --verifier manual entry |
| Phone search never works on KAI | LOW | HIGH | Voice agent prompt prioritizes name-based ID |
| Encrypted field migration breaks existing data | HIGH | LOW | All new fields nullable, no migration of existing data |
8. DEFINITION OF DONE¶
- FreshBay clinic record exists with encrypted per-clinic credentials
- SOAP health check passes against
fbh.kai-oscar.com - At least one provider synced from FreshBay OSCAR
- OAuth access tokens obtained and stored (encrypted)
- Patient search by name returns results
- Appointment booking end-to-end works on FreshBay OSCAR
- Dev OSCAR still works (regression test passed)
NODE_TLS_REJECT_UNAUTHORIZED=0removed from production env- Token expiry monitoring active
- All 7+ pre-launch checks pass for FreshBay clinic