Track Oura recovery next to Strava training in one sheet

Every morning, yesterday's sleep, readiness and workout numbers land as one row in your spreadsheet so you can see how training and recovery move together.

Deterministic Code
OuraStravaGoogle SheetsPersonal ProductivityData SyncDaily Digests
PromptCreate

Every morning at 7am, append yesterday's recovery and training numbers into a single Google Sheets tracker, so that over time I can see how training load and recovery move together. Use a cron trigger set to 7am daily.

First work out yesterday's date in my local timezone and format it as YYYY-MM-DD. Use that same value as both the start date and the end date on every Oura call, since Oura date parameters expect YYYY-MM-DD.

Pull the recovery side from Oura. Use List Daily Sleep for sleep score, total sleep duration and sleep efficiency. Use List Daily Readiness for readiness score, HRV balance and resting heart rate. Use List Daily Activity for steps and active calories. Use List Workouts to count any workouts Oura recorded that day.

Pull the training side from Strava with List Athlete Activities, filtered to yesterday only. Strava filters activities by epoch seconds, so convert yesterday's local start and end of day into epoch timestamps for the before and after parameters, then discard anything that falls outside yesterday. From each activity capture sport type, moving time, distance and average heart rate.

If there is more than one Strava activity on the day, combine them into the one daily row rather than writing multiple rows: join the distinct sport types with a comma, sum moving time, sum distance, and take the average heart rate weighted by each activity's moving time. Ignore activities with no heart rate data when computing that weighted average.

Write exactly one row per day using Append Values, with the Oura recovery columns and the Strava training columns side by side so a single row tells me what I did and how my body responded. Use this column order: Date, Sleep Score, Total Sleep (hours), Sleep Efficiency (%), Readiness Score, HRV Balance, Resting Heart Rate, Steps, Active Calories, Oura Workout Count, Strava Sport Types, Strava Moving Time (minutes), Strava Distance (km), Strava Average Heart Rate. Convert Oura sleep duration from seconds to hours and Strava distance from meters to kilometers, and round each to one decimal place.

Never skip a date. If Oura returns no data for the day, still write the row with the date filled in and the recovery metric cells left blank rather than omitting the date entirely. Do the same on rest days, where the Strava training cells are blank. The date column has to stay continuous or the charts built on this sheet break.

Two things to handle carefully. Oura collection responses paginate, returning a next_token field, so keep requesting with that token until it comes back null before deciding a day has no data. And Gen3 and Ring 4 users need an active Oura Membership or the API simply returns empty data, so treat an empty Oura response as a legitimate blank row rather than an error, and do not fail the run over it.

If the target sheet is empty, write the header row first using the column names above, then append the day's data row beneath it.

What does this prompt do?

  • Adds one row per day to your spreadsheet every morning, covering the day before
  • Puts your recovery numbers (sleep score, total sleep, sleep efficiency, readiness, HRV balance, resting heart rate, steps, active calories) side by side with the training you actually did
  • Pulls each day's sessions (sport, moving time, distance, average heart rate) so a single row tells you what you did and how your body responded
  • Always writes the date, even on days with no ring data or no workout, so your date column stays continuous and charts do not break

What do I need to use this?

  • An Oura account with a ring that is syncing. Gen 3 and Ring 4 owners need an active Oura membership for data to come through
  • A Strava account with your workouts recorded
  • A Google account and a spreadsheet (plus the tab name) where the daily rows should collect

How can I customize it?

  • Change the 7am run time to whatever fits your morning, or run it later if your ring syncs late
  • Add or drop columns, for example bring in body temperature, stress, or elevation gain
  • Point it at a different spreadsheet or tab, or keep separate trackers per training block

FAQs

What happens on a rest day when I do not train?
The row is still written with the date and all of your recovery numbers. The training columns are simply left blank, so you can still chart sleep and readiness across the full period.
What if my ring did not sync or I did not wear it overnight?
The date still gets its own row with blank metric cells rather than being skipped. That keeps the date column unbroken so any charts built on the sheet keep working.
Will this work if I log more than one workout in a day?
Yes. All of that day's sessions are combined into the single daily row, with the sports listed together and the durations and distances totalled.
Do I need a paid Oura membership?
If you have a Gen 3 ring or Ring 4, yes. An active Oura membership is required for your data to be available outside the app. Older rings are not affected.
Can I point this at a spreadsheet that already has data in it?
Yes. New rows are added after the last row already in the sheet, so your existing history stays untouched.
Why does it run in the morning instead of at midnight?
Oura finalises the previous night's sleep and readiness scores after you wake and the ring syncs. Running at 7am gives those numbers time to settle before the row is written.

Related templates

Weekly Amazon S3 bucket security audit posted to Slack

Every Monday, check every S3 bucket for public exposure, missing encryption and weak backup settings, then get the risks ranked in Slack.

Amazon S3
Slack Bot
Google Sheets
Agentic Task
Turn procurement portal tenders into CRM deals each morning

Every weekday at 7am, sign in to the tender portals you track, filter new notices against your bid criteria, and open a deal for the ones worth chasing.

Anchor Browser
Google Sheets
HubSpot
+1
Agentic Task
Draft polite follow-ups for emails that never got a reply

Every weekday at 4pm, spot the threads that went quiet, stage a ready-to-send nudge in your mailbox, and get a ranked Slack recap.

Aurinko
Google Sheets
Slack Bot
Agentic Task
Flag VIP client emails in Zoho Mail and alert your team

Keep a spreadsheet of your most important senders, and every email from one gets labeled, posted to your team channel, and logged automatically.

Zoho Mail
Google Sheets
Slack Bot
Deterministic Code
Send Yotpo review requests when Shopify orders ship

The moment an order is fulfilled, we register it with Yotpo so the review request is timed off real delivery, and we log every sync so failures never go unnoticed.

Shopify
Yotpo
Google Sheets
Deterministic Code
Reconcile Shopify sales against Zoho Books every night

Every night at 11pm we compare the day's Shopify orders with your Zoho Books invoices and payments, and flag only what does not match.

Zoho Books
Shopify
Google Sheets
+1
Agentic Task

Stop guessing why you felt flat today.

Get your recovery and your training in one row per day, built automatically every morning without you touching a spreadsheet.