🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

WHOOP Lab

by @brennaman

Fetch, analyze, chart, and track WHOOP health data (recovery, HRV, RHR, sleep, strain, workouts). Use when: querying any WHOOP metric; generating visual char...

Versionv1.0.0
Downloads596
Comments1
TERMINAL
clawhub install whoop-lab

πŸ“– About This Skill


name: whoop-lab version: 1.0.0 description: "Fetch, analyze, chart, and track WHOOP health data (recovery, HRV, RHR, sleep, strain, workouts). Use when: querying any WHOOP metric; generating visual charts or dashboards; planning, monitoring, or reporting on a health experiment (with auto-captured baselines, post-workout segmentation); logging stats to Obsidian; correlating health data with life context; or proactively flagging suppressed recovery trends. Handles OAuth, token refresh, full history pagination, and science-backed metric interpretation (HRV ranges by age, overtraining signals, sleep stage targets, medication context)." metadata: openclaw: emoji: "πŸ’ͺ" homepage: https://www.paulbrennaman.me/lab/whoop-skill requires: bins: - python3 - git

WHOOP Skill

Fetch, interpret, chart, and track your WHOOP data via the WHOOP Developer API (v2).

Data Directory

All user-specific data is stored in ~/.config/whoop-skill/ β€” separate from the skill install directory, which is read-only.

~/.config/whoop-skill/
  credentials.json   β€” OAuth tokens (created by auth.py on first setup)
  experiments.json   β€” experiment tracking data (created on first plan command)
  config.json        β€” optional path/timezone overrides (copy from config.example.json)

The directory and credentials.json are created automatically when you run scripts/auth.py. You never need to create them manually.

Setup

> Before you begin: This skill requires a WHOOP Developer App to authenticate with the WHOOP API. It's free and takes about 2 minutes to set up.

Step 0 β€” Install Python dependencies

pip install -r requirements.txt

Step 1 β€” Choose your callback method

Before creating your WHOOP app, decide how you want to handle the OAuth callback:

Option A β€” Local server *(local installs)*

  • Redirect URI: http://localhost:8888/callback
  • A temporary server runs on your machine to catch the redirect automatically
  • Requires a browser on the same machine as OpenClaw
  • Option B β€” Manual code paste *(remote/cloud installs)*

  • Redirect URI: http://localhost:8888/callback
  • The script prints an authorization URL β€” open it in any browser, authorize, then copy the ?code= value from the redirect URL and paste it back into the script
  • Nothing passes through any external server β€” fully self-contained
  • Step 2 β€” Create a WHOOP Developer App

    1. Go to https://developer-dashboard.whoop.com 2. Sign in with your WHOOP account 3. Create a Team if prompted (any name works) 4. Click Create App and fill in: - App name: anything (e.g. "My WHOOP Skill") - Redirect URI: the URI from Step 1 (Option A or B) - Scopes: select all read:* scopes + offline 5. Copy your Client ID and Client Secret β€” you'll need them in the next step

    Step 3 β€” Run the setup script

    python3 scripts/auth.py
    

    This will: 1. Prompt you for your Client ID and Client Secret 2. Ask which callback method you chose in Step 1 (local server or manual) 3. Walk you through the authorization flow 4. Save credentials to ~/.config/whoop-skill/credentials.json

    Customize paths (optional): Copy config.example.json from the skill root to ~/.config/whoop-skill/config.json and edit to override defaults:

    {
      "creds_path": "~/.config/whoop-skill/credentials.json",
      "vault_path": "~/my-obsidian-vault",
      "daily_notes_subdir": "Daily Notes",
      "timezone": "America/New_York",
      "logged_by": "Assistant"
    }
    

    Workflow

    1. Load credentials from ~/.config/whoop-skill/credentials.json 2. If expires_at is in the past (or within 60s), call scripts/refresh_token.py to get a new access token and update the file 3. Call the appropriate endpoint (see references/api.md) 4. Parse and present the data in plain language

    Common Requests

  • "How's my recovery today?" β†’ GET latest recovery score, HRV, RHR
  • "How did I sleep?" β†’ GET latest sleep (performance %, stages, duration)
  • "What's my strain today?" β†’ GET latest cycle strain + avg HR
  • "Show my recent workouts" β†’ GET workout collection (last 5–7) via /activity/workout
  • "Give me a health summary" β†’ Combine recovery + sleep + today's cycle
  • Token Refresh

    Run scripts/refresh_token.py when the access token is expired. It reads/writes ~/.config/whoop-skill/credentials.json automatically.

    To re-auth from scratch, run scripts/auth.py again.

    API Base URL

    https://api.prod.whoop.com/developer/v2

    All requests: Authorization: Bearer

    See references/api.md for endpoint details, scopes, and response shapes. For the full official API documentation (including error codes and rate limits), see https://developer.whoop.com/api.


    Fetching Data (scripts/fetch.py)

    General-purpose API fetcher. Used internally by other scripts.

    # Latest recovery
    python3 scripts/fetch.py /recovery --limit 1

    Last 30 days of sleep

    python3 scripts/fetch.py /activity/sleep --limit 30

    Workouts last 7 days

    python3 scripts/fetch.py /activity/workout --limit 7

    Date-range fetch

    python3 scripts/fetch.py /recovery --start 2026-02-01 --end 2026-02-28

    User profile

    python3 scripts/fetch.py /user/profile/basic

    Output is JSON to stdout.


    Charting (scripts/chart.py)

    Generates self-contained HTML charts using Chart.js (CDN). Dark theme with stat cards showing avg/min/max + trend arrow. Opens in browser automatically.

    Chart Types

    | Chart | Description | |-------|-------------| | recovery | Bar chart color-coded green/yellow/red by recovery score | | sleep | Stacked bar: REM / Deep / Light / Awake per night | | hrv | Line chart with 7-day rolling average overlay | | strain | Bar chart with calories as secondary line axis | | dashboard | 2Γ—2 grid of all four charts |

    Usage

    # Recovery chart (30 days)
    python3 scripts/chart.py --chart recovery --days 30

    Full dashboard

    python3 scripts/chart.py --chart dashboard --days 30 --output ~/whoop-dashboard.html

    HRV trend (90 days), don't auto-open

    python3 scripts/chart.py --chart hrv --days 90 --no-open

    Sleep breakdown

    python3 scripts/chart.py --chart sleep --days 14

    Strain + calories

    python3 scripts/chart.py --chart strain --days 21

    Flags

    | Flag | Default | Description | |------|---------|-------------| | --chart | (required) | Chart type: recovery, sleep, hrv, strain, dashboard | | --days | 30 | Days of history to fetch | | --output | /tmp/whoop-.html | Output file path | | --no-open | false | Don't auto-open in browser |

    Chart Delivery (always do both)

    After running chart.py, the script prints the output file path to stdout. Always: 1. Attach the HTML file to the Telegram message β€” so remote users get it instantly 2. Auto-open in browser (default, unless --no-open) β€” so local users get it immediately

    This means both local and remote users are covered without any configuration. The file is self-contained, static, and safe to share β€” no credentials or API calls embedded.


    Experiment Tracking (scripts/experiment.py)

    Define, monitor, and evaluate personal health experiments. Data stored in ~/.config/whoop-skill/experiments.json.

    Supported Metrics

    hrv, recovery, sleep_performance, rhr, strain

    Commands

    #### Plan a new experiment

    python3 scripts/experiment.py plan \
      --name "No alcohol for 30 days" \
      --hypothesis "HRV will increase 10%+ from baseline" \
      --start 2026-03-01 \
      --end 2026-03-31 \
      --metrics hrv,recovery,sleep_performance
    

    Baseline is auto-captured from the 14 days before --start. Override manually:

    python3 scripts/experiment.py plan \
      --name "Cold plunge experiment" \
      --hypothesis "RHR will drop 3+ bpm" \
      --start 2026-03-10 --end 2026-04-10 \
      --metrics hrv,rhr \
      --baseline-hrv 45.0 \
      --baseline-rhr 58
    

    #### Plan with post-workout segmentation

    Use --segment-workouts when your hypothesis is specifically about recovery *after training sessions* rather than overall daily averages. The tracker will fetch your workout history, identify qualifying sessions, and measure recovery metrics only in the 24–48h window after each workout.

    python3 scripts/experiment.py plan \
      --name "My supplement experiment" \
      --hypothesis "Post-strength recovery improves 10%+ vs baseline" \
      --start YYYY-MM-DD --end YYYY-MM-DD \
      --metrics hrv,recovery,rhr \
      --segment-workouts \
      --min-strain 5
    

    Flags:

  • --segment-workouts β€” enables post-workout segmentation mode
  • --min-strain β€” minimum workout strain to qualify (default: 5.0). Filters out light activity like walking or yoga.
  • --days-after β€” recovery window to measure, e.g. 1-2 (days 1 and 2 after workout) or 1 (next day only). Default: 1-2
  • When segmentation is on, status and report show two views: overall rolling averages (all days) and post-workout recovery (only the days after qualifying workouts). The verdict is evaluated against the post-workout view.

    The post-workout baseline is also segmented β€” auto-captured from qualifying workouts in the 14 days before --start β€” so the comparison is apples-to-apples.

    #### Add segmentation to an existing experiment

    python3 scripts/experiment.py add-segmentation \
      --id  \
      --min-strain 5 \
      --days-after 1-2
    

    Patches a previously created experiment to add post-workout segmentation and recomputes the post-workout baseline from the original baseline window.

    #### List experiments

    python3 scripts/experiment.py list
    

    #### Check status (mid-experiment)

    python3 scripts/experiment.py status --id 
    
    Shows current averages vs baseline with % change and trend arrows. If segmentation is enabled, shows both overall and post-workout views with a per-workout breakdown.

    #### Final report

    python3 scripts/experiment.py report --id 
    
    Full before/after comparison, verdict (met / partially met / not met / inconclusive), plain-language summary. Verdict is evaluated on post-workout data when segmentation is on.


    Obsidian Logging (scripts/log_to_obsidian.py) *(optional)*

    > This feature is entirely optional. If you don't use Obsidian, skip this section β€” the rest of the skill works without it. To enable it, set vault_path in ~/.config/whoop-skill/config.json to your Obsidian vault directory. The script will not run if no vault is configured.

    > Note: git is declared as a dependency because the script calls git commands, but it is only ever invoked if your Obsidian vault is a git repository. If the vault directory has no .git folder, the script detects this, writes the daily note, and skips all git commands β€” no errors, no git required in practice.

    Appends today's WHOOP stats to the Obsidian daily note at: /Daily Notes/YYYY-MM-DD.md (configured via vault_path in ~/.config/whoop-skill/config.json)

    After writing, commits and pushes the vault (git add -A && git commit && git push).

    Usage

    # Log today
    python3 scripts/log_to_obsidian.py

    Backfill a specific date

    python3 scripts/log_to_obsidian.py --date 2026-03-01

    Preview without writing

    python3 scripts/log_to_obsidian.py --dry-run

    Output format in daily note

    ## πŸ‹οΈ WHOOP

    | Metric | Value | |--------|-------| | Recovery | 82% πŸ’š | | HRV | 54ms | | Resting HR | 58 bpm | | Sleep Performance | 91% | | Sleep Duration | 7h 42m | | Day Strain | 8.4 |

    _Logged by Assistant at 7:15 AM ET_

  • Creates the daily note if it doesn't exist
  • Skips silently if the WHOOP section already exists
  • Idempotent β€” safe to run multiple times

  • Morning Brief Integration

    Add the following snippet to HEARTBEAT.md to include WHOOP recovery + HRV in morning briefs.

    ## πŸ‹οΈ WHOOP Morning Check

    Run on heartbeats between 06:00–10:00 ET:

    1. Run: python3 scripts/fetch.py /recovery --limit 1 2. Extract records[0].score.recovery_score and records[0].score.hrv_rmssd_milli 3. Include in morning message:

    > πŸ‹οΈ WHOOP β€” Recovery: {score}% {emoji} | HRV: {hrv}ms > > _(Green πŸ’š = push hard. Yellow πŸ’› = moderate. Red πŸ”΄ = rest day.)_

    4. If recovery < 34 (red), mention it proactively even if the user hasn't asked. 5. If Obsidian logging is configured, also run: python3 scripts/log_to_obsidian.py

    Copy-paste ready HEARTBEAT.md snippet:

    ### WHOOP (run once, 06:00–10:00 ET)
    
  • Fetch recovery: python3 scripts/fetch.py /recovery --limit 1
  • Parse recovery_score + hrv_rmssd_milli from records[0].score
  • Report: "πŸ‹οΈ Recovery: {score}% | HRV: {hrv}ms" (add πŸ’š/πŸ’›/πŸ”΄ based on score β‰₯67 / β‰₯34 / <34)
  • If red recovery, mention proactively
  • Log to Obsidian (if configured): python3 scripts/log_to_obsidian.py

  • Health Interpretation

    See references/health_analysis.md for a science-backed guide covering:

  • HRV (RMSSD) ranges by age, what trends mean, red flags
  • Resting heart rate interpretation by fitness level
  • Sleep stage breakdown (deep/REM/light targets, deficit consequences)
  • Recovery score zones (green/yellow/red) and recommended actions
  • Strain scale and how to match strain to recovery
  • SpO2 and skin temperature context
  • Overtraining pattern recognition
  • When to see a doctor

  • References

  • references/api.md β€” Full WHOOP API endpoint reference
  • references/health_analysis.md β€” Health metric interpretation guide
  • WHOOP Developer Dashboard: https://developer-dashboard.whoop.com
  • WHOOP API docs: https://developer.whoop.com/api
  • πŸ’‘ Examples

    # Recovery chart (30 days)
    python3 scripts/chart.py --chart recovery --days 30

    Full dashboard

    python3 scripts/chart.py --chart dashboard --days 30 --output ~/whoop-dashboard.html

    HRV trend (90 days), don't auto-open

    python3 scripts/chart.py --chart hrv --days 90 --no-open

    Sleep breakdown

    python3 scripts/chart.py --chart sleep --days 14

    Strain + calories

    python3 scripts/chart.py --chart strain --days 21

    Flags

    | Flag | Default | Description | |------|---------|-------------| | --chart | (required) | Chart type: recovery, sleep, hrv, strain, dashboard | | --days | 30 | Days of history to fetch | | --output | /tmp/whoop-.html | Output file path | | --no-open | false | Don't auto-open in browser |

    Chart Delivery (always do both)

    After running chart.py, the script prints the output file path to stdout. Always: 1. Attach the HTML file to the Telegram message β€” so remote users get it instantly 2. Auto-open in browser (default, unless --no-open) β€” so local users get it immediately

    This means both local and remote users are covered without any configuration. The file is self-contained, static, and safe to share β€” no credentials or API calls embedded.


    βš™οΈ Configuration

    > Before you begin: This skill requires a WHOOP Developer App to authenticate with the WHOOP API. It's free and takes about 2 minutes to set up.

    Step 0 β€” Install Python dependencies

    pip install -r requirements.txt
    

    Step 1 β€” Choose your callback method

    Before creating your WHOOP app, decide how you want to handle the OAuth callback:

    Option A β€” Local server *(local installs)*

  • Redirect URI: http://localhost:8888/callback
  • A temporary server runs on your machine to catch the redirect automatically
  • Requires a browser on the same machine as OpenClaw
  • Option B β€” Manual code paste *(remote/cloud installs)*

  • Redirect URI: http://localhost:8888/callback
  • The script prints an authorization URL β€” open it in any browser, authorize, then copy the ?code= value from the redirect URL and paste it back into the script
  • Nothing passes through any external server β€” fully self-contained
  • Step 2 β€” Create a WHOOP Developer App

    1. Go to https://developer-dashboard.whoop.com 2. Sign in with your WHOOP account 3. Create a Team if prompted (any name works) 4. Click Create App and fill in: - App name: anything (e.g. "My WHOOP Skill") - Redirect URI: the URI from Step 1 (Option A or B) - Scopes: select all read:* scopes + offline 5. Copy your Client ID and Client Secret β€” you'll need them in the next step

    Step 3 β€” Run the setup script

    python3 scripts/auth.py
    

    This will: 1. Prompt you for your Client ID and Client Secret 2. Ask which callback method you chose in Step 1 (local server or manual) 3. Walk you through the authorization flow 4. Save credentials to ~/.config/whoop-skill/credentials.json

    Customize paths (optional): Copy config.example.json from the skill root to ~/.config/whoop-skill/config.json and edit to override defaults:

    {
      "creds_path": "~/.config/whoop-skill/credentials.json",
      "vault_path": "~/my-obsidian-vault",
      "daily_notes_subdir": "Daily Notes",
      "timezone": "America/New_York",
      "logged_by": "Assistant"
    }