- Node.js >= 18
- npm (comes with Node.js)
- A Supabase project (supabase.com)
- A WHOOP developer account (developer.whoop.com)
- A Withings developer account (developer.withings.com)
- (Optional) A Vercel account for deployment
git clone <your-repo-url> cortex
cd cortex
npm install- Go to supabase.com/dashboard and create a new project
- Note your Project URL and anon (public) key from Settings > API
- Note your Service Role key from Settings > API (keep this secret)
In the Supabase SQL Editor, run the migrations in order:
supabase/migrations/001_create_tables.sql
supabase/migrations/002_enable_rls.sql
Or if using the Supabase CLI:
npx supabase db pushThis creates the following tables:
+---------------------+ +---------------------+
| oauth_tokens | | sync_log |
|---------------------| |---------------------|
| user_id (FK) | | user_id (FK) |
| provider | | provider |
| access_token | | status |
| refresh_token | | records_synced |
| expires_at | | error_message |
+---------------------+ +---------------------+
+---------------------+ +---------------------+
| whoop_cycles | | whoop_recovery |
|---------------------| |---------------------|
| user_id (FK) | | user_id (FK) |
| whoop_cycle_id | | whoop_cycle_id |
| strain | | recovery_score |
| kilojoule | | resting_heart_rate |
| average_heart_rate | | hrv_rmssd_milli |
| max_heart_rate | | spo2_percentage |
| calories_kcal | | skin_temp_celsius |
+---------------------+ +---------------------+
+---------------------+ +---------------------+
| whoop_sleep | | whoop_workouts |
|---------------------| |---------------------|
| user_id (FK) | | user_id (FK) |
| whoop_sleep_id | | whoop_workout_id |
| total_*_time_milli | | sport_id |
| respiratory_rate | | strain |
| sleep_performance_% | | average_heart_rate |
| sleep_efficiency_% | | calories_kcal |
+---------------------+ +---------------------+
+---------------------+
| withings_measure... |
|---------------------|
| user_id (FK) |
| withings_grpid |
| weight_kg |
| fat_ratio_pct |
| muscle_mass_kg |
| bone_mass_kg |
| bmi |
+---------------------+
- In Supabase Dashboard, go to Authentication > Providers
- Enable Google provider
- Add your Google OAuth Client ID and Secret
- Set the redirect URL to:
https://<your-supabase-project>.supabase.co/auth/v1/callback
- Go to developer.whoop.com
- Create a new application
- Set the Redirect URI to:
http://localhost:3000/api/auth/whoop/callback(and your production URL when deploying) - Note the Client ID and Client Secret
- Required scopes:
read:profile,read:body_measurement,read:cycles,read:recovery,read:sleep,read:workout,offline
- Go to developer.withings.com
- Create a new application
- Set the Callback URL to:
http://localhost:3000/api/auth/withings/callback(and your production URL when deploying) - Note the Client ID and Client Secret
- Required scopes:
user.metrics,user.activity
Copy the example env file and fill in your values:
cp .env.example .env.localEdit .env.local:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# WHOOP OAuth
WHOOP_CLIENT_ID=your-whoop-client-id
WHOOP_CLIENT_SECRET=your-whoop-client-secret
# Withings OAuth
WITHINGS_CLIENT_ID=your-withings-client-id
WITHINGS_CLIENT_SECRET=your-withings-client-secret
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Cron (generate a random secret for production)
CRON_SECRET=your-random-secretnpm run devOpen http://localhost:3000. You'll be redirected to the login page.
- Sign in with Google on the login page
- Go to Connections page
- Connect WHOOP - you'll be redirected to WHOOP to authorize
- Connect Withings - you'll be redirected to Withings to authorize
- After connecting, an initial sync runs automatically (pulls last 30 days)
- Your dashboard will populate with charts and metrics
| Issue | Solution |
|---|---|
| "Unauthorized" on connect | Make sure you're logged in first |
| OAuth redirect fails | Check your redirect URIs match exactly in the provider dashboards |
| No data after connecting | Click the sync button in the header, or wait for the sync to complete |
| Supabase RLS errors | Make sure you ran both migration files (001 and 002) |
| Token refresh fails | Verify your client secrets are correct in .env.local |
| Build fails | Run npm install to ensure all dependencies are installed |