Skip to main content

Junction Wearable Integration

How reflex-junction connects wearable health data to Syntropy-Journals.
Package: reflex-junction (PyPI: pip install reflex-junction) Status: Active — 5 providers available (Oura, WHOOP, Fitbit, Garmin, Dexcom)

Architecture

┌──────────────┐     ┌──────────────┐     ┌──────────────────┐
│ Settings Page│────►│ JunctionState│────►│ Junction/Vital   │
│ Link Modal   │     │ (Reflex)     │     │ API (wearables)  │
└──────────────┘     └──────┬───────┘     └──────────────────┘

                    ┌───────▼────────┐
                    │ JunctionService│  ← lifecycle/DI
                    │ (BaseService)  │
                    └───────┬────────┘

                    ┌───────▼────────┐
                    │ Diet Scoring   │
                    │ + wearable     │
                    │   bonus (0-15) │
                    └────────────────┘

Environment Variables

VariableRequiredDefaultDescription
JUNCTION_API_KEYNo(empty)Junction/Vital API key. If absent, all wearable features are silently disabled.
JUNCTION_ENVIRONMENTNosandboxOne of: sandbox, production, sandbox_eu, production_eu
Set in envs/base (shared) or envs/dev (override):
JUNCTION_API_KEY=sk_us_...
JUNCTION_ENVIRONMENT=sandbox
Also stored in Infisical (Syntropy App project, dev environment).

How It’s Wired

1. JunctionService (lifecycle)

syntropy_journals/app/api/services/junction_service.py — a BaseService registered in the ServiceRegistry alongside LLM, Chat, Diet, and Slack services.
  • initialize_sync() — reads env vars at module import time (before async loop)
  • wire_reflex_app(app) — calls junction.wrap_app() if API key is present
  • is_available — property used by other code to check if Junction is active
Called in syntropy_journals/syntropy_journals.py after rx.App() creation:
_junction_svc = JunctionService()
_junction_svc.initialize_sync()
_junction_svc.wire_reflex_app(app)

2. Clerk → Junction user bridge

syntropy_journals/app/states/app/settings/_processes.pybridge_junction_user() handler:
  • Runs on Settings page on_load
  • Gets clerk_user_id from AuthState
  • Creates a Junction user mapped to that ID (one-time, idempotent)
  • Wrapped in try/except for graceful degradation
syntropy_journals/app/components/shared/integration_cards.pyjunction_link_modal():
  • Opens when user clicks “Connect” on an available provider card
  • Shows junction_link_button with a generated link token
  • On success, JunctionState.on_provider_connected fires (auto-refreshes providers)

4. Diet scoring wearable bonus

syntropy_journals/app/functions/diet/scoring.pycompute_wearable_bonus():
  • Additive bonus (0-15 points) on top of the diet base score (0-100)
  • Sleep quality: avg sleep score / 10, max 10 pts
  • Activity level: avg daily steps / 2000, max 5 pts
  • Users without wearables get bonus = 0 (no regression)
Formula: composite = min(diet_base + wearable_bonus, 100)

Available Providers

ProviderIDTypeJunction-supported
Oura RingourawearableYes (beta)
WHOOPwhoopwearableYes
FitbitfitbitwearableYes
GarmingarminwearableYes
Dexcom CGMdexcomcgmYes
Apple Healthapple_healthappComing soon
Google Health Connectgoogle_health_connectappComing soon
Epic MyChartepic_mychartehrComing soon
Gmail Receiptsgmail_receiptsemailComing soon

Testing

# Wearable bonus scoring tests
APP_ENV=test .venv/bin/python -m pytest tests/unit/test_wearable_bonus.py -v -o "addopts="

# Integration schema tests (provider availability)
APP_ENV=test .venv/bin/python -m pytest tests/unit/test_integration_source_schema.py -v -o "addopts="
  • #101 — initial wiring + wearable scoring
  • #104 — Link widget, Clerk bridge, JunctionService
  • reflex-junction #4 — Phase 2+4 (Link widget, guard components)