# PPHrisk SMART-on-FHIR — Deployment Guide

PPHrisk implements **SMART App Launch 2.0** with read-only patient scopes. This document covers (1) testing in the public sandbox, (2) registering against your hospital's Epic / Cerner, and (3) the FHIR data PPHrisk reads.

## Architecture

```
EHR (Epic / Cerner)
    │
    │  1. Provider clicks "PPHrisk" inside chart
    │     → Hyperspace launches https://pphrisk.com/launch.html
    │        with ?launch=<token>&iss=<fhir-server>
    │
    ▼
PPHrisk launch.html
    │
    │  2. Calls FHIR.oauth2.authorize() with patient scopes
    │     → Redirects user to EHR's OAuth endpoint
    │     → User implicitly authenticated by EHR session
    │
    ▼
EHR OAuth → redirect to https://pphrisk.com/index.html?code=...&state=...
    │
    │  3. index.html detects ?code= → handleSmartLaunch()
    │     → FHIR.oauth2.ready() completes handshake
    │     → smart-prefill.js fetches Patient, Condition, Observation,
    │       MedicationStatement, MedicationRequest, Procedure
    │
    ▼
Risk factors auto-checked in calculator UI
SMART badge displayed: "patient · 7 factors prefilled from chart"
```

No FHIR data is persisted to PPHrisk's servers. The access token lives in `sessionStorage` and is gone when the tab closes.

## Files

| File | Role |
|---|---|
| `launch.html` | SMART launch entry point — kicks off OAuth |
| `smart-prefill.js` | Maps FHIR resources to PPHrisk factor IDs |
| `index.html` | Detects OAuth callback + runs prefill + shows SMART badge |

## Phase 1 — Test against the public SMART sandbox (5 minutes)

1. Open https://launch.smarthealthit.org
2. **Launch URL:** `https://pphrisk.com/launch.html`
3. **Client ID:** `pphrisk-app` (already configured in `launch.html`)
4. **FHIR Version:** R4
5. **Scopes:** leave default (the launcher will request what we ask for)
6. Click **Launch**
7. Select a patient — try `smart-1288992` (Pregnancy) or `smart-1577780` (Diabetes + Hypertension)
8. You should land on `https://pphrisk.com/?code=...&state=...` and see the SMART badge populate

Patient prefill expectations:
- `smart-1288992` should hit: `priorPPH`, `chronicHTN`, possibly `obesity` and `preeclampsia`
- `smart-1577780` should hit: `chronicHTN`, `obesity`

If nothing prefills, check browser console for `[SMART]` logs.

## Phase 2 — Register at your hospital's Epic

Each Epic instance has its own FHIR base URL and client registration. Two paths:

### Path A — Internal app (4–8 weeks at a single hospital)

This is what you want for a **single-hospital pilot**. The hospital's Epic team registers PPHrisk privately, only their users can launch it.

Send your Epic analyst this packet:

```
App name: PPHrisk
App vendor: Epigenuity LLC
Launch URL: https://pphrisk.com/launch.html
Redirect URI: https://pphrisk.com/index.html
SMART version: SMART App Launch 2.0
FHIR version: R4
Required scopes (read-only):
  launch
  openid profile fhirUser
  patient/Patient.read
  patient/Condition.read
  patient/Observation.read
  patient/MedicationStatement.read
  patient/MedicationRequest.read
  patient/Encounter.read
  patient/Procedure.read
Activity logging: no PHI persisted by PPHrisk
Public client (no client_secret) — token request via PKCE
```

The analyst will provide:
- A **client_id** specific to your Epic instance
- A **FHIR base URL** to test against
- An **activation date** for production

**Action:** in `launch.html`, change `clientId: 'pphrisk-app'` to the client_id they issue. (For now keep multiple branches per-hospital, or read from URL params — see "Multi-tenant" below.)

### Path B — Epic Showroom (formerly App Orchard, 6–12 months)

Public listing, all Epic hospitals can discover and self-install.

- **Cost:** ~$2.5K application + ~$15K/year membership
- **Requires:** SOC 2 Type II, technical architecture doc, marketing assets, demo videos
- **Process:** apply at https://showroom.epic.com → security review → technical certification → contract
- **Worth it after:** 3+ hospitals are asking

## Phase 3 — Register at your hospital's Cerner (now Oracle Health)

Cerner is faster and cheaper than Epic.

1. Free developer sandbox: https://code-console.cerner.com — sign up, register an app, get a client_id
2. Test launch URL: pick a Cerner sample patient and exercise the flow against the sandbox
3. Production deployment: ~$3K initial + per-deployment fees
4. Marketplace listing: https://code.cerner.com (self-service compared to Epic)

## Multi-tenant client IDs (when you have >1 hospital)

Today `launch.html` hardcodes `clientId: 'pphrisk-app'`. To support multiple Epic instances:

```javascript
// In launch.html, replace clientId with a lookup keyed by `iss`
const ISS = new URLSearchParams(location.search).get('iss') || '';
const CLIENT_IDS = {
  'https://fhir.hospital-epic.example.com/': 'pphrisk-prod',
  'https://fhir.hospital-epic-2.example.com/': 'pphrisk-prod-2',
  // ...
};
FHIR.oauth2.authorize({
  clientId: CLIENT_IDS[ISS] || 'pphrisk-app',
  scope: '...',
  redirectUri: 'index.html'
});
```

The `iss` (issuer) URL is passed by the EHR on launch and uniquely identifies the FHIR server.

## What PPHrisk reads from FHIR

`smart-prefill.js` defines `FACTOR_RULES` mapping FHIR codes to PPHrisk factor IDs. Summary:

| PPHrisk factor | FHIR Resource | Code system |
|---|---|---|
| `priorPPH` | Condition | ICD-10 O72.x, SNOMED 47821001 |
| `multiple` | Condition | ICD-10 O30.x, Z37.2–Z37.7 |
| `priorCS` | Condition / Procedure | ICD-10 O34.21, CPT 59510/59514/59515 |
| `fibroids` | Condition | ICD-10 D25.x, O34.1 |
| `obesity` | Observation | LOINC 39156-5 (BMI ≥40) |
| `anemia` | Observation | LOINC 4544-3 (Hct <30) |
| `previa` | Condition | ICD-10 O44.x |
| `accreta` | Condition | ICD-10 O43.21/.22/.23 |
| `lowPlt` | Observation | LOINC 777-3, 26515-7 (<100) |
| `coagulopathy` | Condition + MedicationStatement | ICD-10 D68/D69 + RxNorm anticoagulants |
| `chronicHTN` | Condition | ICD-10 I10–I15, O10 |
| `gestHTN` | Condition | ICD-10 O13 |
| `preeclampsia` | Condition | ICD-10 O14.0, O14.9 |
| `severePreeclampsia` | Condition | ICD-10 O14.1, O14.2 |
| `chorio` | Condition | ICD-10 O41.12 |
| `polyhydramnios` | Condition | ICD-10 O40 |
| `macrosomia` | Observation / Condition | LOINC 11727-5 (≥4000g), ICD-10 O36.6/P08.0 |
| `mag` | MedicationStatement | RxNorm Magnesium sulfate |

When a resource also has free-text in `code.text`, regex matchers serve as a fallback for hospitals that haven't fully coded everything.

## Privacy + audit posture

- **No PHI persisted** by PPHrisk in Phase 1
- Access token in `sessionStorage` only — destroyed on tab close
- All FHIR requests use the patient-scoped token (no roaming patient access)
- Intrapartum and active-bleeding factors are still entered by the clinician (no automation)
- Hero badge clearly labels the session as SMART-launched

To upgrade to Phase 2 (write-back chart notes), the app will need a BAA with the hospital and switch to confidential client (server-side token exchange).

## Local testing

You can run `launch.html` against the public sandbox without deploying anywhere:

```bash
cd /Users/ravigoyal/Desktop/All_Data/Research/websites/pphrisk-site
python3 -m http.server 8000
# then visit https://launch.smarthealthit.org and use
# Launch URL: http://localhost:8000/launch.html
```

For Vercel preview deploys (each git push creates a unique URL), use that preview URL in the sandbox launcher to test changes before promoting.

## Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| `Could not launch from your EHR` on `launch.html` | `fhir-client.min.js` failed to load | Check network; allowlist `cdn.jsdelivr.net` |
| Stuck on "Connecting to your EHR…" | EHR OAuth endpoint blocked / wrong client_id | Verify client_id with Epic analyst; check browser dev tools Network tab for the failed `/authorize` redirect |
| Launches but no factors prefill | FHIR server returned 0 conditions, or coding differs | Open dev tools console; look for `[SMART]` logs. If FHIR resources come back but no matches, the hospital may use proprietary code systems — add the codes to `FACTOR_RULES` in `smart-prefill.js` |
| `code=` in URL but page doesn't react | `SMART_KEY` not in sessionStorage (user opened the redirect URL directly, bypassing the launch) | This is expected — direct URL access doesn't auth. Launch from sandbox or EHR. |
| Patient match wrong | Multiple FHIR resources matched the same factor (acceptable — first match wins) | No action |

## Next steps after pilot

1. **Phase 2 — DocumentReference write-back** of the chart note PPHrisk generates today (currently copy-paste only)
2. **CDS Hooks** integration so PPHrisk can fire alerts during admission orders
3. **Bulk FHIR ($export)** for compliance reporting at the unit level
4. **Smart Health Cards** for handoff documentation

These all require write-scope and an active hospital BAA.
