Back to Investor Brief

Investor Brief · Part 2

JupyterHealth Integration: open substrate, menopause-specific layer

Pause-Health.ai is designed as the menopause intelligence layer on top of JupyterHealth's open FHIR R5 substrate. Today two of the five JupyterHealth packages (omh-shim + jupyterhealth-client) are integrated and unit-tested in the Python ingest worker; the JHE server itself, the SMART-on-FHIR Epic launch, and the Helm-chart deployment are design-stage. The architectural punchline — JHE stores the data and runs consent, Pause does the reasoning — is the target end state, anchored against the Phase 0 baseline below.

What we adopt · status-pilled

Five JupyterHealth packages, two wired today

Pills: Wired in prototype integrated in the Python ingest worker today · Designed committed choice, activates in Phase 1 or later.

Wired in prototype

JupyterHealth Exchange (JHE)

jupyterhealth/jupyterhealth-exchange

  • What it doesFHIR R5 + Open mHealth data plane. OAuth2/OIDC, scope-based consent, multi-tenant orgs and studies. Django app deployable into a customer VPC.
  • Why it mattersWe adopt this instead of building it. Customers see open standards, not a black box. Stood up locally on 2026-06-16 (Docker postgres + JHE Django container, OIDC RS256 key, seeded Study + Patient + Oura Data Source + client_credentials OAuth app + FhirSource row) and pause_ingest's examples/oura_sample_upload.py round-trips both a raw Oura heart-rate sample and a derived HRV-time-domain feature set (with derivedFrom pointer to the raw row) through omh-shim → FHIR R5 Observation → POST → readback against it. Both JHE write paths exercised: the raw OMH coding routes to JHE's mapped Observation handler (integer pk), the derived `https://pause-health.ai/schemas/derived` coding routes to the auxiliary FhirAuxResource handler (UUID pk) via the `X-JHE-FHIR-Source-ID` header. Transcript at docs/JHE_REAL_RUN_2026-06-16.md.
Wired in prototype

jupyterhealth-client

jupyterhealth/jupyterhealth-client

  • What it doesPython client (pip install jupyterhealth-client). Used by the Pause backend to read patient observations from the Exchange.
  • Why it mattersLibrary, not service. Adds zero ops surface. Integrated today in pause_ingest.exchange (read + upload paths); a passing test asserts the read shape against a fixture.
Wired in prototype

omh-shim

jupyterhealth/omh-shim

  • What it doesConverters from vendor wearable JSON (Oura, Open Wearables) to Open mHealth / IEEE 1752.1.
  • Why it mattersLets us normalize wearable data before ingest. Integrated today in pause_ingest.convert against omh-shim v1.0.1, with an explicit allow-list and 20 passing unit tests. Apple HealthKit + skin_temperature converters are planned contributions back.
Designed

jupyter-smart-on-fhir

jupyterhealth/jupyter-smart-on-fhir

  • What it doesReference SMART-on-FHIR launch flow from an EHR into a Jupyter / web environment.
  • Why it mattersPattern for our Epic / Cerner-embedded launch. Same auth model as the rest of the platform. Today: studied, not yet wired in the prototype.
Designed

helm-charts

jupyterhealth/helm-charts

  • What it doesKubernetes deployment for JHE — public cloud, private cloud, or on-prem behind a firewall.
  • Why it mattersHow customer health systems run the substrate inside their own VPC. Pause's inference layer runs alongside. Phase 5 work; no Pause-tuned values.yaml committed yet.

Wearable data types we surface

Six menopause-relevant data types

Data typeMenopause relevance
Heart rateVasomotor signal; sympathetic drive
Heart rate variabilityAutonomic dysregulation, sleep quality
Sleep duration / sleep episodeNight sweats, sleep fragmentation
Oxygen saturationSleep-disordered breathing. Supported by omh-shim today for Open Wearables; Oura converter is a near-term upstream PR.
Step count / physical activityFatigue, activity drop
Skin temperature (gap)Hot-flash signal not yet in omh-shim v1.0.1. Natural Pause contribution back upstream — converter plus Open mHealth schema proposal.

Touch the architecture

Three live surfaces you can hit right now

The FHIR R5 Bundle shape JupyterHealth Exchange will produce in a customer deployment is already served as a deterministic mock at /api/mulesoft/patient/[id]/timeline. It returns a Patient + raw wearable Observations + DBDP-computed feature Observation with a derivedFrom reference back to the raw window — the same lineage every Pause recommendation will be auditable against.

Patient timeline (FHIR R5 Bundle) →pause_ingest source + tests →Full design doc →

Phased integration plan

From the Phase 0 baseline to a customer-VPC deployment

Wired in prototype

Phase 0 · Today

Shipped

omh-shim and jupyterhealth-client integrated in pause_ingest. Vendor JSON → Open mHealth (IEEE 1752.1) → FHIR R5 Observation path is end-to-end runnable in the Python worker, including DBDP HRV feature computation → derived Observation upload with derivedFrom provenance. Wire-level contract test (tests/jhe_mock_server.py + test_exchange_integration.py) exercises the full pipeline against an in-process JHE mock — the production exchange.upload_observation, hrv_features_to_fhir_observation, and read_recent_observations code paths run unmodified. The same contract assertions also run against a real JHE instance via an opt-in PAUSE_USE_REAL_JHE=1 pytest marker (tests/conftest.py + tests/test_exchange_real_jhe.py); 7 / 7 real-JHE tests green against the jhe-local Docker stack. Surfaced a real bug in the jupyterhealth-client integration that lenient unit-test doubles missed, plus two more mock-vs-real divergences on the first real-mode run (POST response shape; unknown-patient list filter) now pinned in both directions. Reference FHIR R5 Bundle shape also served live at /api/mulesoft/patient/anika-patel/timeline.

Wired in prototype

Phase 1 · Local dev loop against real JHE

Shipped 2026-06-16 (extended 2026-06-23)

Real JupyterHealth Exchange Django container brought up against a Docker postgres on the maintainer's box, OIDC RS256 key generated, seeded with the canonical RBAC fixtures, an OAuth client_credentials app named pause-ingest bound to a real Patient + Oura DataSource through a Study with explicit per-scope consent rows + a FhirSource row, and pause_ingest's full pipeline ran end-to-end through BOTH JHE write paths: the raw OMH heart-rate observation routes to JHE's mapped Observation handler (server-issued integer pk) and the derived HRV-time-domain feature observation (with derivedFrom pointer to the raw row's server id) routes to the auxiliary FhirAuxResource handler (server-issued UUID pk) via the X-JHE-FHIR-Source-ID header threaded through IngestConfig.fhir_source_id. Three real-JHE-only gotchas the wire-level mock had not pinned were surfaced and fixed in the same session: pause_ingest was requesting OAuth scope strings (observation.read / observation.write) JHE's vocabulary rejects with invalid_scope; pause_ingest used Content-Type: application/fhir+json which JHE's parser rejects (must be application/json); and pause_ingest's OMH coding system / code shape did not match JHE's mapped-Observation routing criteria (system https://w3id.org/openmhealth, code omh:<schema>:<version>), so writes silently fell through to the auxiliary handler which then 400'd on the missing X-JHE-FHIR-Source-ID header. The wire-level mock now mirrors the routing contract (codings outside https://w3id.org/openmhealth require the header) so future drift on either write path fails the contract test instead of silently routing wrong against real JHE. Extended 2026-06-23 by shipping the JHE_SETUP_RUNBOOK's Path B sketch as a real opt-in pytest marker — PAUSE_USE_REAL_JHE=1 swaps the in-process JheMockServer fixture for IngestConfig.from_env() and runs the same 7 contract assertions against the live jhe-local stack; 7/7 green on the first real-mode invocation. Two more wire-level surprises the mock had papered over were surfaced in that run and pinned in both directions: real JHE's POST /Observation response omits valueAttachment (only the envelope; mock echoed the full posted resource); real JHE's GET /Observation?patient=<unknown> returns the OAuth client's full authorized set, not an empty Bundle. Full transcript at docs/JHE_REAL_RUN_2026-06-16.md; runbook Path B at docs/JHE_SETUP_RUNBOOK.md.

Designed

Phase 2 · Real wearable ingest

2–3 weeks

Vendor OAuth for Oura, then HealthKit bridge for Apple Health. Background worker polls samples and runs the convert → upload pipeline at scale.

Designed

Phase 3 · Provider read path

2–3 weeks

FastAPI assembles a patient timeline via jupyterhealth-client. Wire the menopause classifier and RAG over the guideline corpus. Clinician view in the Pause web app.

Designed

Phase 4 · Provider write path

3–4 weeks

Write Observation, CarePlan, and DocumentReference back to JHE. Capture clinician accept / edit / reject as outcomes-registry events.

Future

Phase 5 · Customer-VPC deployment

4+ weeks per design partner

Deploy JHE into the customer VPC via Helm. Wire SAML SSO. Deploy Pause inference alongside in federated mode — model weights leave the VPC; PHI does not.

Strategic value

Four reasons the substrate is JupyterHealth

Open contributions back

Four upstream contributions Pause plans to ship

Part of the strategy, not an afterthought. Earning standing in the JupyterHealth and Open mHealth communities lowers procurement friction for every subsequent customer.

Plan Plan items — none of these contributions are merged upstream yet.

Read deeper

Where the JupyterHealth substrate sits in the bigger picture