All-in-one local development tool for cloud wearable integrations — Complete CLI + FastAPI server + ngrok integration for WHOOP, Garmin, and Fitbit testing.
The Synheart Wear CLI combines a command-line interface with an embedded FastAPI server for local wearable development:
- 🔧 Manage OAuth tokens (list, refresh, revoke)
- 📥 Pull data from cloud APIs
- 🔍 Inspect webhook events
- 🚀 Start/stop local dev server
- 🔐 OAuth flows for cloud wearables (WHOOP, Fitbit, Garmin*)
- 🪝 Webhook endpoints for real-time data
- 🌐 Automatic ngrok tunnel exposure
- 💾 Local token storage (dev mode) or DynamoDB + KMS (production)
- 📊 Data normalization to Synheart format
Perfect for:
- Testing SDK integrations locally
- Developing apps with WHOOP/Garmin data
- Prototyping cloud wearable features
- Full OAuth flow testing
* Garmin support in development
- Python 3.11+
- ngrok account (free): https://ngrok.com/
- Wearable API credentials (WHOOP, Garmin, etc.)
- AWS Account (optional for production token storage)
Option A: Install from PyPI (Recommended)
# Install the CLI globally
pip install synheart-wear-cli
# Verify installation
wear version
wear --helpOption B: Install from Source
# Clone the repository
git clone https://github.com/synheart-ai/synheart-wear-cli.git
cd synheart-wear-cli
# Install in development mode
pip install -e ".[dev]"
# Verify installation
python3 wear.py --help
# Or if installed globally:
wear --helpNote: All required libraries are automatically installed. No separate cloning needed.
# Get your auth token from https://dashboard.ngrok.com/get-started/your-authtoken
ngrok config add-authtoken YOUR_TOKENCreate .env.local in the CLI directory:
# WHOOP Credentials
WHOOP_CLIENT_ID=your_whoop_client_id
WHOOP_CLIENT_SECRET=your_whoop_client_secret
# AWS (optional - for production token storage)
AWS_REGION=us-east-1
DYNAMODB_TABLE=synheart-wear-tokens
KMS_KEY_ID=alias/synheart-wear
# Development Mode (automatically enabled by CLI)
DEV_MODE=true
WEBHOOK_RECORD=true# Start WHOOP connector with ngrok
python3 wear.py start dev --vendor whoop --port 8000
# Or with auto-open browser for OAuth:
python3 wear.py start dev --vendor whoop --open-browser
# The CLI will automatically:
# ✅ Start local FastAPI server
# ✅ Start ngrok tunnel
# ✅ Display ngrok URL for SDK configuration
# ✅ Enable webhook recording
# ✅ Setup hot-reload for code changesOutput:
🚀 Starting Synheart Wear
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📍 Configuration:
Mode: dev
Vendor: whoop
Port: 8000
Auto-reload: ✅ enabled
Webhook record: ✅ enabled
📝 Auto-loaded environment from: .env.local
🌐 Endpoints:
API Docs: http://localhost:8000/docs
Health Check: http://localhost:8000/health
OAuth Auth: http://localhost:8000/v1/oauth/authorize
Webhooks: http://localhost:8000/v1/webhooks/whoop
🌐 Starting ngrok tunnel...
✅ ngrok tunnel started: https://abc123-xyz.ngrok-free.app
📱 SDK Configuration:
Use this URL in your Flutter app:
baseUrl: 'https://abc123-xyz.ngrok-free.app'
Start local server with automatic ngrok tunneling.
# Start WHOOP connector
python3 wear.py start dev --vendor whoop --port 8000
# Start with auto-open browser for OAuth
python3 wear.py start dev --vendor whoop --open-browser
# Start unified service (all vendors - Garmin coming soon)
python3 wear.py start dev --port 8000
# Use specific environment file
python3 wear.py start dev --vendor whoop --env .env.production
# Disable auto-reload
python3 wear.py start dev --vendor whoop --no-reload
# Verbose logging
python3 wear.py start dev --vendor whoop --verboseOptions:
--vendor, -v- Vendor to run (whoop,garmin, or omit for all)--port, -p- Port to run on (default: 8000)--reload/--no-reload- Auto-reload on code changes (default: enabled)--env- Environment file to load (.env.local,.env.production)--open-browser- Automatically open OAuth authorization URL--webhook-record/--no-webhook-record- Enable webhook recording (default: enabled)--verbose- Enable verbose logging
What it does:
- ✅ Starts FastAPI server on specified port
- ✅ Automatically starts ngrok tunnel
- ✅ Displays ngrok URL for SDK configuration
- ✅ Enables auto-reload for code changes
- ✅ Records webhooks to
__dev__/webhooks_recent.jsonl
Pull data from vendor cloud API (requires OAuth connection first).
# Pull WHOOP recovery data (last 7 days)
python3 wear.py pull once --vendor whoop --since 7d
# Pull specific data types
python3 wear.py pull once --vendor whoop --since 30d --data-types recovery,sleep,workouts
# Pull from specific user
python3 wear.py pull once --vendor whoop --user-id abc123 --since 14dOptions:
--vendor- Vendor to pull from (required)--since- Time range (e.g.,7d,30d,2h)--data-types- Comma-separated data types (recovery, sleep, workouts, cycles)--user-id- Specific user ID (optional)
List, refresh, or revoke OAuth tokens.
# List all tokens
python3 wear.py tokens list
# List tokens for specific vendor
python3 wear.py tokens list --vendor whoop
# Refresh expired token
python3 wear.py tokens refresh --vendor whoop --user-id abc123
# Revoke token (disconnect user)
python3 wear.py tokens revoke --vendor whoop --user-id abc123Inspect webhook events recorded during development.
# Inspect recent webhooks (last 50)
python3 wear.py webhook inspect --limit 50
# Filter by vendor
python3 wear.py webhook inspect --vendor whoop --limit 100
# Show webhook details
python3 wear.py webhook inspect --verbosesynheart-wear-cli/
├── wear.py # Main CLI entry point
├── server/ # Local development server
│ ├── whoop_api.py # WHOOP OAuth + data endpoints
│ ├── garmin_api.py # Garmin OAuth + data endpoints
│ ├── unified_api.py # Unified service (all vendors)
│ └── whoop_connector.py # WHOOP connector logic
├── libs/
│ ├── py-cloud-connector/ # OAuth token management
│ └── py-normalize/ # Data normalization
└── __dev__/ # Development data (auto-generated)
├── webhooks_recent.jsonl
└── tokens.json
python3 wear.py start dev --vendor whoop --open-browserThe browser will open automatically. Log in and authorize.
Use the ngrok URL displayed in the terminal in your app:
Flutter:
final whoopProvider = WhoopProvider(
baseUrl: 'https://abc123-xyz.ngrok-free.app',
redirectUri: 'synheart://oauth/callback',
);Swift:
let whoopProvider = WhoopProvider(
baseUrl: URL(string: "https://abc123-xyz.ngrok-free.app")!,
redirectUri: "synheart://oauth/callback"
)Kotlin:
val whoopProvider = WhoopProvider(
baseUrl = "https://abc123-xyz.ngrok-free.app",
redirectUri = "synheart://oauth/callback"
)Once connected, fetch data from your app or use the CLI:
python3 wear.py pull once --vendor whoop --since 7dWebhooks are automatically recorded to __dev__/webhooks_recent.jsonl:
python3 wear.py webhook inspect --limit 10The local server exposes these endpoints:
GET /v1/oauth/authorize- Get OAuth authorization URLGET /v1/oauth/callback- OAuth callback (GET)POST /v1/oauth/callback- OAuth callback (POST, mobile)POST /v1/webhooks/whoop- Webhook handlerDELETE /v1/oauth/disconnect- Disconnect userGET /v1/data/{user_id}/recovery- Fetch recovery dataGET /v1/data/{user_id}/sleep- Fetch sleep dataGET /v1/data/{user_id}/workouts- Fetch workout dataGET /v1/data/{user_id}/cycles- Fetch cycle data
GET /v1/whoop-cloud/oauth/authorize- WHOOP OAuthGET /v1/garmin-cloud/oauth/authorize- Garmin OAuthPOST /v1/whoop-cloud/webhooks/whoop- WHOOP webhooksPOST /v1/garmin-cloud/webhooks/garmin- Garmin webhooks
GET /health- Service health status
API Docs: http://localhost:8000/docs
- Token Storage: DynamoDB with KMS encryption (production)
- Dev Mode: Tokens stored locally in
__dev__/tokens.json(development) - Webhook Verification: HMAC signature validation
- Environment Variables: Never commit
.env.localfiles
# Run tests
pytest
# Run specific test
pytest tests/test_oauth.py -v
# Run with coverage
pytest --cov=server --cov-report=htmlProblem: "ngrok endpoint already online"
Solution:
# Kill all ngrok processes
pkill -f ngrok
# Or restart with different port
python3 wear.py start dev --port 8001Problem: "Port 8000 is already in use"
Solution:
# Find process using port
lsof -i :8000
# Kill process
kill $(lsof -ti :8000)
# Or use different port
python3 wear.py start dev --port 8001Problem: "Authentication failed"
Solution:
- Check environment variables in
.env.local - Verify redirect URI matches vendor configuration
- Check ngrok URL is correct
- Look at server logs for detailed errors
OAuth token management for wearable vendors with DynamoDB + KMS encryption.
Features:
VendorType: Enum for supported vendors (whoop, garmin, fitbit)TokenStore: DynamoDB-based token storage with encryptionTokenSet: Standardized OAuth token data structure
See libs/py-cloud-connector/README.md
Data normalization utilities for converting vendor-specific formats to Synheart format.
Features:
DataNormalizer: Converts vendor data to common formatDataType: Enum for data types (recovery, sleep, workout, etc.)NormalizedData: Common data structure for all vendors
See libs/py-normalize/README.md
- Main Repository: synheart-wear
- Flutter SDK: synheart-wear-dart
- Android SDK: synheart-wear-kotlin
- iOS SDK: synheart-wear-swift
- ngrok: https://ngrok.com/
- Issues: GitHub Issues
MIT License - see LICENSE file
Made with ❤️ by the Synheart AI Team
Technology with a heartbeat.